Tue, 05 Feb 2008 18:59:29 +0100
refactoring: moved everything of the library to a "pygpibtoolkit" module.
--- a/HP3562A/__init__.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -""" -HP3562A -======= - -Module for communicating with the HP 3562A Digital Signal Analyzer. - -Subpackages ------------ - - -Constants ---------- - -""" -import struct -import re -import pygpib as gpib -import numpy - -######################################## -# HP3562A internal binary types decoders - -def decode_float(s): - if len(s) == 4: - i1, i2, e = struct.unpack('>hbb', s) - i2 = i2 * 2**(-23) - return (i1/32768. + i2)*2**e - else: - i1, i2, i3, i4, e = struct.unpack('>hhhbb', s) - if i2 < 0: - i2 = (i2+32768.)*2**(-15) - else: - i2 = i2*2**(-15) - if i3 < 0: - i3 = (i3+32768.)*2**(-30) - else: - i3 = i3*2**(-15) - i4 = i4 * 2**(-38) - return (i1+i2+i3+i4)*2**(e-15) - -# def decode_float(s): -# assert len(s) in [4,8] -# # exponential term -# e = ord(s[-1]) -# if e & 0x80: -# e = e - 256 - -# # mantissa -# m = [ord(x) for x in s[:-1]] -# M = 0. -# for i in range(len(s)-1): -# #M += m[i]<<(i*8) -# M += float(m[i])/2**((i+1)*8) -# # XXX how do we deal negative numbers? -# #if m[0] & 0x80: -# # M = M - 2^(len(s)) -# return M * 2**(e+1) - -def decode_string(s): - nb = ord(s[0]) - s = s[1:nb+2] - r = "" - # XXX why do we need to do this? It's not described in the manual... - for c in s: - r += chr(ord(c) & 0x7F) - return r - - -### -# Some useful functions -def format_header(header, head_struct, columns=80): - """ - Pretty print a data block (trace, state or coord) - """ - bool_re = re.compile(r'((?P<before>.*) )?(?P<flag>\w+/\w+)( (?P<after>.*))?') - - todisp = [] - for row in head_struct: - key = row[0] - typ = row[1] - if typ is None: - continue - val = header.get(key, "N/A") - if isinstance(val, basestring): - val = repr(val) - elif typ is bool and isinstance(val, typ): - m = bool_re.match(key) - if m: - d = m.groupdict() - key = "" - if d['before']: - key += d['before'] - if d['after']: - key += d['after'] - key = key.capitalize() - val = d['flag'].split('/')[not val] - else: - val = str(val) - else: - val = str(val) - todisp.append((key+":", val)) - maxk = max([len(k) for k, v in todisp]) - maxv = max([len(v) for k, v in todisp]) - fmt = "%%-%ds %%-%ds"%(maxk, maxv) - w = maxk+maxv+4 - ncols = columns/w - if ncols: - nrows = len(todisp)/ncols - else: - nrows = len(todisp) - res = "" - for i in range(nrows): - res += "| ".join([fmt%todisp[j*nrows+i] for j in range(ncols)]) + "\n" - return res - -def decode_header(data, header_struct, idx=0): - d = data - if d[idx:].startswith('#'): - # we have a preliminary header here... - typ = d[idx:idx+2] - assert typ == "#A" - idx += 2 - totlen = struct.unpack('>h', d[idx:idx+2])[0] - idx += 2 - tt=0 - header = {} - for i, (nam, dtype, fmt, nbytes) in enumerate(header_struct): - if dtype is None: - idx += nbytes - continue - elif dtype == str: - val = decode_string(d[idx:]) - else: - if fmt: - v = struct.unpack('>'+fmt, d[idx: idx+nbytes])[0] - if isinstance(dtype, dict): - val = dtype.get(int(v), "N/A") - else: - val = dtype(v) - else: - val = dtype(d[idx: idx+nbytes]) - header[nam] = val - idx += nbytes - return header, idx - -def read_trace(data, idx, nelts): - assert len(data[idx:]) >= (nelts*4), "data[idx:] is too small (%s for %s)"%(len(data[idx:]), (nelts*4)) - resu = [] - for i in range(nelts): - resu.append(decode_float(data[idx: idx+4])) - idx += 4 - return numpy.array(resu, dtype=float) - -##################### -# HP3562A constants - -# GPIB buffer size is 3x80 characters lines -class STATUS_BYTE(gpib.Constants): - # HP3562A Status Byte, as returned by a serial poll - _constants = [(0x40, "RQS", "Request Service"), # when sent in response to a serial poll - (0x20, "ERR", "GPIB error"), - (0x10, "RDY", "ready to accept GPIB commands"), - ] - - conditions = [(0, "NSR", "No service requested"), - (1, "USRQ1", "User SRQ #1"), - (2, "USRQ1", "User SRQ #2"), - (3, "USRQ1", "User SRQ #3"), - (4, "USRQ1", "User SRQ #4"), - (5, "USRQ1", "User SRQ #5"), - (6, "USRQ1", "User SRQ #6"), - (7, "USRQ1", "User SRQ #7"), - (8, "USRQ1", "User SRQ #8"), - (9, "EOD", "End of disk action"), - (10, "EOP", "End of plot action"), - (11, "STCH", "Instrument status changed"), # any change in - # the status register sets this condition - (12, "PWR", "Power on"), - (13, "KEY", "Key pressed"), - (14, "DCP", "Device clear plotter (listen)"), - # ... - ] - def __init__(self): - super(STATUS_BYTE, self).__init__() - self._conditions = dict([(x[0], x[1]) for x in self.conditions]) - self._rev_conditions = dict([(x[1], x[0]) for x in self.conditions]) - self._long_conditions = dict([(x[0], x[2]) for x in self.conditions]) - - def byte_condition(self, byte): - byte = byte & 0x8F - return self._conditions.get(byte, "N/A") - -class IS_REGISTER(gpib.Constants): - _constants = [(0x01, "MEASP", "measeurement pause"), - (0x02, "ASQP", "Auto sequence pause"), - (0X04, "EOM", "End of measurement, capture or throughput"), - (0x08, "EOAS", "End of auto sequence"), - (0x10, "SWPR", "Sweep point ready"), - (0x20, "CH1OV", "Channel 1 overrange"), - (0x40, "CH2OV", "Channel 2 overrange"), - (0X80, "CH1HR", "Channel 1 half range"), - (0x100, "CH2HR", "Channel 2 half range"), - (0x200, "SFALT", "Source falt"), - (0x400, "RUNL", "Reference unlock"), - (0x800, "RMKT", "Remote marker knob turn"), - (0x1000, "REKT", "Remote entry knob turn"), - (0x2000, "ASRC", "Asctive Status Register changed"), - (0x4000, "PWRF", "Power-on test failed"), - ] - -class StatusQuery(gpib.Constants): - _command = "STA?" - _constants = [(0x01, "N/A", "Not used"), - (0x02, "N/A", "Not used"), - (0x04, "KEY", "Key pressed"), - (0x08, "N/A", "Not used"), - (0x10, "RDY", "Ready"), - (0x20, "ERR", "Error"), - (0x40, "RQS", "Request"), - (0x80, "MOS", "Message on screen"), - (0x100, "MEASP", "measeurement pause"), - (0x200, "ASQP", "Auto sequence pause"), - (0X400, "EOM", "End of measurement, capture or throughput"), - (0x800, "EOAS", "End of auto sequence"), - (0x1000, "SWPR", "Sweep point ready"), - (0x2000, "CH1OV", "Channel 1 overrange"), - (0x4000, "CH2OV", "Channel 2 overrange"), - (0x8000, "MAOV", "Math overflow"), - ] -class ActivityStatysRegister(gpib.Constants): - _command = "AS?" - _constants = [(0x01, "CKFL", "Check fault log"), - (0x02, "FITR", "Filling time record"), - (0x04, "FLTR", "Filters settings"), - (0x08, "CFTP", "Curve fir in progress"), - (0x10, "MSSM", "Missed sample"), - (0x20, "TMPR", "Timed preview"), - (0x40, "ACDA", "Accept date"), - #... - ] - -
--- a/HP3562A/coord_decoder.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -# -*- coding: utf-8 -*- -""" -state_decoder -============= - -Module for decoding the internal state of the HP3562A DSA, using the -GPIB command "DSBN" (Dump State BiNary). - -This file can be exectued as a python script. Use '-h' for more informations. -""" - -from HP3562A import format_header, decode_float, decode_string, decode_header, read_trace -from HP3562A.trace_decoder import decode_trace, HEADER as TRACE_HEADER -from HP3562A.enum_types import * - -HEADER = [("Y coordinates", EYCOORD, 'h', 2), - ("# of disp elements", int, "h", 2), - ("First element", int, "h", 2), - ("Total elements", int, 'h', 2), - ("Display sampling", EDISPSMP, 'h', 2), - ("Scaling", ESCAL, 'h', 2), - ("Data pointer", long, 'l', 4), - ("In data", long, 'l', 4), - ("Log/linear x-axis", bool, 'h', 2), - ("Sampled display data", bool, 'h', 2), - ("Plot/Graph mode", bool, 'h', 2), - ("Phase wrap", bool, 'h', 2), - ("Not used", None, None, 36), - ("X scale factor", decode_float, None, 4), - ("Grid min Y scale", decode_float, None, 4), - ("Grid max Y scale", decode_float, None, 4), - ("/div", decode_float, None, 4), - ("Min value of data", decode_float, None, 4), - ("Max value of data", decode_float, None, 4), - ("Y cumulative min", decode_float, None, 4), - ("Y cumulative max", decode_float, None, 4), - ("Y scale factor", decode_float, None, 4), - ("Not used", None, None, 16), - ("Stop value", decode_float, None, 8), - ("Left grid", decode_float, None, 8), - ("Right grid", decode_float, None, 8), - ("Left data", decode_float, None, 8), - ("Right data", decode_float, None, 8), - ] - -def decode_coord(data): - """ - Decode the data (as generated by the HP3562A DSA in response to a - "DSBN" command), and returns a dict (header) - - header is the dictionnary of the header of the dumped data block, - """ - header, idx = decode_header(data, HEADER) - trace_header, idx = decode_header(data, TRACE_HEADER, idx) - trace = read_trace(data, idx, trace_header["Number of elements"]) - return header, trace_header, trace - - -def main(): - import sys - import optparse - import numpy - - opt = optparse.OptionParser("A simple tool for displaying dumped coord data block") - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, read from stdin') - opt.add_option('-m', '--mode', default='binary', - dest='mode', - help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', - ) - opt.add_option('-d', '--display-header', default=False, - action="store_true", - dest="displayheader", - help="Display the trace header") - opt.add_option('-P', '--noplot-trace', default=True, - action="store_false", - dest="plot", - help="Do not display the plot of the trace") - opt.add_option('-x', '--xmode', default='lin', - dest='xmode', - help='X coordinate mode (may be "lin" [default] or "log")') - opt.add_option('-y', '--ymode', default='lin', - dest='ymode', - help='Y coordinate mode (may be "lin" [default], "log" or "db")') - - options, argv = opt.parse_args(sys.argv) - - - if options.filename is None: - print "Can't deal stdin for now..." - sys.exit(1) - try: - coord_header, header, data = decode_coord(open(options.filename, 'rb').read()) - except Exception, e: - print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename - print e - sys.exit(1) - - if options.displayheader: - print format_header(coord_header, HEADER, 100) - print format_header(header, TRACE_HEADER, 100) - if options.plot: - f0 = header['Start freq value'] - dx = header['Delta X-axis'] - n = header['Number of elements'] - x = numpy.linspace(f0, f0+dx*n, len(data)) - y = data.copy() - - ym = coord_header['Min value of data'] - yM = coord_header['Max value of data'] - ys = coord_header['Y scale factor'] - - y[y<ym] = ym - y *= ys - - import pylab - pylab.ylabel(header['Amplitude units']) - pylab.grid() - pylab.plot(x, y) - pylab.xlabel('frequency (%s)'%header["X axis units"]) - pylab.ylabel("%s (%s)"%(coord_header['Y coordinates'], header['Amplitude units']) ) - pylab.show() - -if __name__ == "__main__": - main()
--- a/HP3562A/dump_datablock.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -import sys -import time -import gpib -import prologix - - -class HP3562dumper(prologix.GPIB): - """ - Class dedicated to dump data blocks from HP3562A DSA (trace, - internal state or coordinate). - """ - MODES = {'trace': 'DD', - 'state': 'DS', - 'coord': 'DC', - } - - FORMATS = {'binary': 'BN', - 'ascii': 'AS', - 'ansi': 'AN'} - - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=0): - super(HP3562dumper, self).__init__(device, baudrate, timeout, address, mode=1) - - def dump(self, mode='trace', format="binary"): - """ - Dump the required data block and return it as a raw string buffer. - - 'mode' can be 'trace' [default], 'state' or 'coord'. - 'format' can be 'binary' [default], 'ascii' or 'ansi'. - """ - format = format.lower() - mode = mode.lower() - assert mode in self.MODES - assert format in self.FORMATS - cmd = self.MODES[mode] + self.FORMATS[format] - - res = "" - print "command = ", cmd - self._cnx.write('%s\r'%cmd) - i = 0 - while i<self._retries: - l = self._cnx.readline() - if l.strip() == "": - i += 1 - time.sleep(self._timeout) - continue - res += l - i = 0 - return res - - - -def main(): - import optparse - opt = optparse.OptionParser("A simple tool for dumping the current trace") - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, write to stdout') - opt.add_option('-d', '--device', default='/dev/ttyUSB0', - dest='device', - help='Device of the RS232 connection (default: /dev/ttyUSB0)', - ) - opt.add_option('-a', '--address', default=0, - dest='address', - help='GPIB address of the device', - ) - opt.add_option('-b', '--block', default='trace', - dest='block', - help='Data block to dump (may be "trace" [default], "state" or "coord")', - ) - opt.add_option('-m', '--mode', default='binary', - dest='mode', - help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', - ) - options, argv = opt.parse_args(sys.argv) - - cnx = HP3562dumper(device=options.device, address=int(options.address)) - res = cnx.dump(mode=options.block, format=options.mode) - sys.stderr.write("read %s bytes\n"%(len(res))) - if options.filename: - open(options.filename, 'w').write(res) - else: - print res - -if __name__=='__main__': - main()
--- a/HP3562A/enum_types.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Constants used for HP3562A data block interpretation. -""" - -EDSP = {0: "No data", - 1: "Frequency response", - 2: "Power spectrum 1", - 3: "Power spectrum 2", - 4: "Coherence", - 5: "Cross spectrum", - 6: "Input time 1", - 7: "Input time 2", - 8: "Input linear spectrum 1", - 9: "Input linear spectrum 2", - 10: "Impulse response", - 11: "Cross correlation", - 12: "Auto correlation 1", - 13: "Auto correlation 2", - 14: "Histogram 1", - 15: "Histogram 2", - 16: "Cumulative density function 1", - 17: "Cumulative density function 2", - 18: "Probability density function 1", - 19: "Probability density function 2", - 20: "Average linear spectrum 1", - 21: "Average linear spectrum 2", - 22: "Average time record 1", - 23: "Average time record 2", - 24: "Synthesis pole-zeros", - 25: "Synthesis pole-residue", - 26: "Synthesis polynomial", - 27: "Synthesis constant", - 28: "Windowed time record 1", - 29: "Windowed time record 2", - 30: "Windowed linear spectrum 1", - 31: "Windowed linear spectrum 2", - 32: "Filtered time record 1", - 33: "Filtered time record 2", - 34: "Filtered linear spectrum 1", - 35: "Filtered linear spectrum 2", - 36: "Time capture buffer", - 37: "Captured linear spectrum", - 38: "Captured time record", - 39: "Throughput time record 1", - 40: "Throughput time record 2", - 41: "Curve fit", - 42: "Weighted function", - 43: "Not used", - 44: "Orbits", - 45: "Demodulation polar", - 46: "Preview demod record 1", - 47: "Preview demod record 2", - 48: "Preview demod linear spectrum 1", - 49: "Preview demod linear spectrum 2", - } - -ECH = {0: "Channel 1", - 1: "Channel 2", - 2: "Channel 1&2", - 3: "No channel", - } - -EOVR = ECH - -EDOM = {0: 'Time', - 1: 'Frequency', - 2: 'Voltage (amplitude)', - } - -EVLT = {0: "Peak", - 1: "RMS", - 2: "Volt (indicates peak only)", - } - -EAMP = {0: u"Volts", - 1: u"Volts²", - 2: u"PSD (V²/Hz)", - 3: u"ESD (V²s/Hz)", - 4: u"PSD¹² (V/Hz¹²)", - 5: u"No unit", - 6: u"Unit volts", - 7: u"Unit volts²", - } - -EXAXIS= {0: u"No units", - 1: u"Hertz", - 2: u"RPM", - 3: u"Orders", - 4: u"Seconds", - 5: u"Revs", - 6: u"Degrees", - 7: u"dB", - 8: u"dBV", - 9: u"Volts", - 10: u"V\u221AHz", - 11: u"Hz/s", - 12: u"V/EU", - 13: u"Vrms", - 14: u"V²/Hz", - 15: u"%", - 16: u"Points", - 17: u"Records", - 18: u"Ohms", - 19: u"Hertz/octave", - 20: u"Pulse/Rev", - 21: u"Decades", - 22: u"Minutes", - 23: u"V²s/Hz", - 24: u"Octave", - 25: u"Seconds/Decade", - 26: u"Seconds/Octave", - 27: u"Hz/Point", - 28: u"Points/Sweep", - 29: u"Points/Decade", - 30: u"Points/Octave", - 31: u"V/Vrms", - 32: u"V²", - 33: u"EU referenced to chan 1", - 34: u"EU referenced to chan 2", - 35: u"EU value", - } - -EMEAS = {0: "Linear resolution", - 1: "Log resolution", - 2: "Swept sine", - 3: "Time capture", - 4: "Linear resolution throughput", - } - -EDEMOD = {45: "AM", - 46: "FM", - 47: "PM", - } - -EAVG = {0: "No data", - 1: "Not averaged", - 2: "Averaged",} - -EWIN = {0: "N/A", - 1: "Hann", - 2: "Flat top", - 3: "Uniforme", - 4: "Exponential", - 5: "Force", - 6: "Force chan 1/expon chan 2", - 7: "Expon chan 1/force chan 2", - 8: "User", - } - -EMTYP = {0: "Frequency responce", - 1: "Cross correlation", - 2: "Power spectrum", - 3: "Auto correlation", - 4: "Histogram", - 5: "No measurement", - } - -EWINTYP = { 11: "Hanning", - 12: "Flat top", - 13: "Uniform", - 14: "User window", - 15: "Force/Exponential", - } - -EFEXPW = { 0: "Force", - 1: "Exponential", - } - -EAVGTYP = { 6: "Stable", - 7: "Exponential", - 8: "Peak", - 9: "Continuous peak", - 10: "Averaging off", - } - -ETRGTYP = { 18: "Free run", - 19: "Channel 1", - 20: "Channel 2", - 21: "External", - 22: "Source trigger", - 23: "HP-IB trigger", - } - -ETRGSLP = { 16: "Positive", - 17: "Negative", - } - -EPRVTYP = { 0: "Manual preview", - 1: "Timed preview", - 2: "Preview off", - } - -ESMPTYP = { 24: "Internal sample", - 25: "External sample", - } - -ERNGUNT = EXAXIS - -ERNGTYP = { 26: "Auto range on", - 27: "Auto range off", - 28: "Auto range set", - } - -EINCPL = { 29: "AC", - 30: "DC", - } - -ESRCTYP = { 31: "Source off", - 32: "Random noise", - 33: "Burst random", - 34: "Periodic chirp", - 35: "Burst chirp", - 36: "Swept sine", - 37: "Fixed sine", - } - -ESWPDIR = { 41: "Up", - 42: "Sweep hold", - 43: "Manual sweep", - 44: "Down", - } - -ESWPMOD = { 39: "Linear sweep", - 40: "Log sweep", - } - -EEXTSMPFREQUNT = EXAXIS - -EBNDUNT = EXAXIS - -ESWPRATEUNT = EXAXIS - -EAUGAINREFCH = { 0: "Channel 1", - 1: "Channel 2", - 2: "Not used", - 3: "No channel", - } -EDEMODCH = { 0: "Channel 1", - 1: "Channel 2", - 2: "Both channels", - 3: "No channel", - } - -ESRCLVLUNT = EXAXIS - -ESRCDCOFFST = EXAXIS - -ETRGLVLUNT = EXAXIS - -ECPTLGHUNT = EXAXIS - -EYCOORD = { 1: "Real", - 2: "Imaginary", - 3: "Linear magnitude", - 4: "Log magnitude", - 5: "dB", - 6: "Nyquist", - 7: "Not used", - 8: "Phase", - 9: "Nichols", - 10: "dBm", - } - -EDISPSMP = { 0: "Not sampled", # #displayed elements = total elements - 1: "Half sampled", # #displayed elements = total elements/2 - 2: "Sampled", # #displayed elements < total elements - } -ESCAL = { 0: "X and Y auto scale", - 1: "X fixed, Y auto scale", - 2: "X auto scale, Y fixed", - 3: "X and Y fixed", - }
--- a/HP3562A/q3562A.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,346 +0,0 @@ -# - -import os, sys -import time -import glob - -from PyQt4 import QtGui, QtCore, uic -from PyQt4.QtCore import SIGNAL, Qt - -if "-m" in sys.argv: - from dump_datablock_mockup import HP3562dumper -else: - from dump_datablock import HP3562dumper -import q3562A_rc - -ldir = os.path.abspath(os.path.dirname(__file__)) -sys.path.append(ldir) -form_class, base_class = uic.loadUiType(os.path.join(ldir, "q3562A.ui")) - -from qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem -from qpreferences import PointItem, SizeItem, ByteArrayItem -from qpreferences import AbstractPreferences -from qpreferenceseditor import PreferencesEditor - -from tools import str_num_cmp - -class Preferences(AbstractPreferences): - ORGANISATION="Logilab" - APPLICATION="qgpib_plotter" - - _pos = PointItem() - _size = SizeItem() - _appState = ByteArrayItem() - - device = UnicodeItem(default='/dev/ttyUSB0', - name=u'device', - description=u'GPIB device', - group="GPIB settings") - address = IntItem(default=5, min=0, max=16, - name=u'GPIB address', - group="GPIB settings") - - background = ColorItem(default=QtGui.QColor("white"), - name="Background", - group="Colors") - color0 = ColorItem(default=QtGui.QColor("black"), - name="Pen #0", - group="Colors") - color1 = ColorItem(default=QtGui.QColor("green"), - name="Pen #1", - group="Colors") - color2 = ColorItem(default=QtGui.QColor("red"), - name="Pen #2", - group="Colors") - color3 = ColorItem(default=QtGui.QColor("blue"), - name="Pen #3", - group="Colors") - color4 = ColorItem(default=QtGui.QColor("yellow"), - name="Pen #4", - group="Colors") - color5 = ColorItem(default=QtGui.QColor("cyan"), - name="Pen #5", - group="Colors") - color6 = ColorItem(default=QtGui.QColor("magenta"), - name="Pen #6", - group="Colors") - color7 = ColorItem(default=QtGui.QColor("darkred"), - name="Pen #7", - group="Colors") - -class QtHPGLPlotter(QtGui.QMainWindow, form_class): - def __init__(self, parent=None): - QtGui.QMainWindow.__init__(self, parent) - self._plots = {} - self._prefs = Preferences() - self.setupUi() - self.initializeGPIB() - if self._prefs._pos: - self.move(self._prefs._pos) - if self._prefs._size: - self.resize(self._prefs._size) - if self._prefs._appState: - self.restoreState(self._prefs._appState) - self.readPreferences() - - def readPreferences(self): - bg = self._prefs.background - if bg and bg.isValid(): - self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg)) - pen_colors = [self._prefs["color%d"%i] for i in range(8)] - self.plotterWidget.pen_colors = pen_colors - - def replotCurrent(self): - self.currentPlotChanged(self.plotsView.currentIndex()) - - def setupUi(self): - form_class.setupUi(self, self) # call qtdesigner generated form creation - # actions defined in designer - self.connect(self.actionPreferences, SIGNAL('triggered(bool)'), - self.preferencesTriggered) - self.connect(self.actionQuit, SIGNAL('triggered(bool)'), - self.quitTriggered) - self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q')) - self.connect(self.actionOpen, SIGNAL('triggered(bool)'), - self.openTriggered) - self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O')) - self.connect(self.actionSave, SIGNAL('triggered(bool)'), - self.saveTriggered) - self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S')) - self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'), - self.saveAsTriggered) - - self.plotterWidget = QHPGLPlotterWidget(self) - self.setCentralWidget(self.plotterWidget) - - self.connect(self.captureButton, SIGNAL("toggled(bool)"), - self.captureToggled) - - self._plots_list = QtGui.QStringListModel() - self.plotsView.setModel(self._plots_list) - self.connect(self.plotsView, SIGNAL('activated(const QModelIndex&)'), - self.currentPlotChanged) - self.connect(self.plotsView.selectionModel(), - SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)'), - self.currentPlotChanged) - - def currentPlotChanged(self, index, old_index=None): - if index.isValid(): - value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) - - self.plotterWidget.clear() - self.plotterWidget.parse(self._plots[value]) - - def preferencesTriggered(self, checked=False): - PreferencesEditor(self._prefs, self).exec_() - self.readPreferences() - self.replotCurrent() - - def quitTriggered(self, checked=False): - self.close() - - def closeEvent(self, event): - if 1: - #if self.promptForSave(): - self._prefs._pos = self.pos() - self._prefs._size = self.size() - self._prefs._appState = self.saveState() - event.accept() - else: - event.ignore() - - def openTriggered(self, checked=False): - filenames = QtGui.QFileDialog.getOpenFileNames(self, "Open a HPGL file to display", '.', 'HPGL files (*.plt)\nAll files (*)') - self.openFiles(filenames) - self.displayFirst() - - def displayFirst(self): - if not self.plotsView.currentIndex().isValid(): - self.plotsView.setCurrentIndex(self.plotsView.model().index(0, 0)) - - def openFiles(self, filenames): - ok = False - for filename in filenames: - filename = str(filename) - if os.path.exists(filename): - data = open(filename).read() - name = os.path.basename(filename) - name = os.path.splitext(name)[0] - lst = self.plotsView.model().stringList() - lst.append(name) - self._plots[name] = data - self.plotsView.model().setStringList(lst) - ok = True - return ok - - def plotReceived(self, num): - self._receiving = False - self.setReceivingLed() - plot, timestamp = self.captureThread.getPlot(num) - name = "plot_%s"%(num) - lst = self.plotsView.model().stringList() - lst.append(name) - self._plots[name] = plot - self.plotsView.model().setStringList(lst) - - def plotStarted(self): - self._receiving = True - self.setReceivingLed() - - def saveTriggered(self, checked=False): - print "save" - - def saveAsTriggered(self, checked=False): - index = self.plotsView.selectionModel().currentIndex() - if index.isValid(): - filename = QtGui.QFileDialog.getSaveFileName(self, "Selecte a file name to save HPGL file", '.', 'HPGL files (*.plt)\nAll files (*)') - n = index.row() - value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) - open(filename, 'w').write(self._plots[value]) - - - def initializeGPIB(self): - self._online = False - try: - self.gpib_plotter = QGPIBplotter(device=self._prefs.device, - address=self._prefs.address, - ) - self.captureThread = GPIBReceiver(self.gpib_plotter) - self.connect(self.captureThread, SIGNAL('plotReceived(int)'), - self.plotReceived) - self.connect(self.captureThread, SIGNAL('plotStarted()'), - self.plotStarted) - self.captureThread.start() - except Exception, e: - #print e - self.gpib_plotter = None - self.setCaptureLed() - - def captureToggled(self, state): - if state: - if self.gpib_plotter is None: - self.initializeGPIB() - if self.gpib_plotter is None: - QtGui.QMessageBox.critical(self, self.tr("GPIB error"), - self.tr("<b>Unable to initialize GPIB connection</b>.<br>Please check your GPIB dongle and settings.")) - self._online = False - self.setCaptureLed() - return - self._online = True - self.captureThread.startCapture() - else: - if self.captureThread: - self.captureThread.stopCapture() - self._online = False - self.setCaptureLed() - - def setCaptureLed(self): - if self._online: - icn = QtGui.QIcon(':/icons/led_green.svg') - else: - icn = QtGui.QIcon(':/icons/led_green_off.svg') - self.captureButton.setIcon(icn) - self.captureButton.setChecked(self._online) - - def setReceivingLed(self): - if self._receiving: - icn = QtGui.QIcon(':/icons/led_red.svg') - else: - icn = QtGui.QIcon(':/icons/led_red_off.svg') - self.receivingButton.setIcon(icn) - -class QGPIBplotter(GPIBplotter): - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=5): - GPIBplotter.__init__(self, device, baudrate, timeout, address) - self.emitter = None - - def plotStarted(self): - if self.emitter: - self.emitter.emit(SIGNAL('plotStarted()')) - #self.emitter.msleep(1) - -class GPIBReceiver(QtCore.QThread): - def __init__(self, cnx): - QtCore.QThread.__init__(self) - self.gpibplotter = cnx - self.gpibplotter.emitter = self - - self._cancelmutex = QtCore.QMutex() - self._cancel = False - #self._nreceived = 0 - self._plotsmutex = QtCore.QMutex() - self._plots = [] - self._startstopmutex = QtCore.QMutex() - self._startstop = QtCore.QWaitCondition() - self._capturing = False - - def cancel(self): - self._cancelmutex.lock() - self._cancel = True - self._cancelmutex.unlock() - - def startCapture(self): - self._startstop.wakeOne() - - def stopCapture(self): - self._startstopmutex.lock() - self._capturing = False - self._startstopmutex.unlock() - - def run(self): - while 1: - self._cancelmutex.lock() - if self._cancel: - return - self._cancelmutex.unlock() - self._startstopmutex.lock() - if not self._capturing: - self._startstop.wait(self._startstopmutex) - self._capturing = True - self._startstopmutex.unlock() - - plot = self.gpibplotter.load_plot(wait_timeout=0.1) - timestamp = time.time() - if plot: - self._plotsmutex.lock() - self._plots.append((plot, timestamp)) - n = len(self._plots) - self._plotsmutex.unlock() - self.emit(SIGNAL('plotReceived(int)'), n-1) - self.msleep(10) - - def getPlot(self, num): - self._plotsmutex.lock() - try: - return self._plots[num] - finally: - self._plotsmutex.unlock() - -def main(): - import optparse - opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') - opt.add_option('-m', '--mockup', default=False, - action="store_true", - dest='mockup', - help='Use a pseudo GPIB connection (for test purpose)', - ) - opt.add_option('-v', '--verbose', default=False, - action="store_true", - dest="verbose", - help="Verbose mode",) - - options, argv = opt.parse_args(sys.argv) - - a = QtGui.QApplication(argv) - w = QtHPGLPlotter() - files = [f for f in argv[1:] if os.path.isfile(f)] - files.sort(cmp=str_num_cmp) - if w.openFiles(files): - w.displayFirst() - - w.show() - a.exec_() - -if __name__ == '__main__': - main()
--- a/HP3562A/q3562A.ui Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ -<ui version="4.0" > - <class>MainWindow</class> - <widget class="QMainWindow" name="MainWindow" > - <property name="geometry" > - <rect> - <x>0</x> - <y>0</y> - <width>507</width> - <height>492</height> - </rect> - </property> - <property name="windowTitle" > - <string>MainWindow</string> - </property> - <widget class="QWidget" name="centralwidget" /> - <widget class="QMenuBar" name="menubar" > - <property name="geometry" > - <rect> - <x>0</x> - <y>0</y> - <width>507</width> - <height>25</height> - </rect> - </property> - <widget class="QMenu" name="menu_File" > - <property name="title" > - <string>&File</string> - </property> - <addaction name="actionOpen" /> - <addaction name="actionSave" /> - <addaction name="actionSaveAs" /> - <addaction name="separator" /> - <addaction name="actionQuit" /> - </widget> - <widget class="QMenu" name="menuEdit" > - <property name="title" > - <string>Edit</string> - </property> - <addaction name="actionPreferences" /> - </widget> - <addaction name="menu_File" /> - <addaction name="menuEdit" /> - </widget> - <widget class="QStatusBar" name="statusbar" /> - <widget class="QDockWidget" name="plotsDock" > - <attribute name="dockWidgetArea" > - <number>1</number> - </attribute> - <widget class="QWidget" name="dockWidgetContents" > - <layout class="QVBoxLayout" > - <property name="spacing" > - <number>4</number> - </property> - <property name="leftMargin" > - <number>1</number> - </property> - <property name="topMargin" > - <number>4</number> - </property> - <property name="rightMargin" > - <number>1</number> - </property> - <property name="bottomMargin" > - <number>1</number> - </property> - <item> - <layout class="QHBoxLayout" > - <item> - <widget class="QToolButton" name="getTraceButton" > - <property name="minimumSize" > - <size> - <width>0</width> - <height>29</height> - </size> - </property> - <property name="text" > - <string>get trace</string> - </property> - <property name="icon" > - <iconset/> - </property> - <property name="iconSize" > - <size> - <width>16</width> - <height>16</height> - </size> - </property> - <property name="checkable" > - <bool>true</bool> - </property> - <property name="toolButtonStyle" > - <enum>Qt::ToolButtonTextBesideIcon</enum> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="getStatusButton" > - <property name="minimumSize" > - <size> - <width>0</width> - <height>29</height> - </size> - </property> - <property name="text" > - <string>get status</string> - </property> - <property name="checkable" > - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="getCoordButton" > - <property name="minimumSize" > - <size> - <width>0</width> - <height>29</height> - </size> - </property> - <property name="text" > - <string>get coord</string> - </property> - <property name="checkable" > - <bool>true</bool> - </property> - </widget> - </item> - <item> - <spacer> - <property name="orientation" > - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" > - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <widget class="QListView" name="objectsView" /> - </item> - </layout> - </widget> - </widget> - <action name="actionOpen" > - <property name="text" > - <string>&Open</string> - </property> - </action> - <action name="actionSave" > - <property name="text" > - <string>&Save</string> - </property> - </action> - <action name="actionSaveAs" > - <property name="text" > - <string>Save as...</string> - </property> - </action> - <action name="actionQuit" > - <property name="text" > - <string>&Quit</string> - </property> - </action> - <action name="actionPreferences" > - <property name="text" > - <string>Preferences</string> - </property> - </action> - </widget> - <resources> - <include location="q3562A.qrc" /> - </resources> - <connections/> -</ui>
--- a/HP3562A/state_decoder.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- -""" -state_decoder -============= - -Module for decoding the internal state of the HP3562A DSA, using the -GPIB command "DSBN" (Dump State BiNary). - -This file can be exectued as a python script. Use '-h' for more informations. -""" -from HP3562A import format_header, decode_float, decode_string, decode_header - -from HP3562A.enum_types import * - -HEADER = [("Measurement mode", EMEAS, 'h', 2), - ("Measurement 1", EMTYP, 'h', 2), - ("Measurement 2", EMTYP, 'h', 2), - ("Window type", EWINTYP, 'h', 2), - ("Force/Expon window 1", EFEXPW, 'h', 2), - ("Force/Expon window 2", EFEXPW, 'h', 2), - ("Average type", EAVGTYP, 'h', 2), - ("Overlap percentage", int, 'h', 2), - ("Number of averages", int, 'h', 2), - ("Sweep # of averages", int, 'h', 2), - ("Trigger type", ETRGTYP, 'h', 2), - ("Trigger slope", ETRGSLP, 'h', 2), - ("Preview type", EPRVTYP, 'h', 2), - ("Sample type", ESMPTYP, 'h', 2), - ("Range units chan 1", ERNGUNT, 'h', 2), - ("Range units chan 2", ERNGUNT, 'h', 2), - ("Range type 1", ERNGTYP, 'h', 2), - ("Range type 2", ERNGTYP, 'h', 2), - ("Input coupling 1", EINCPL, 'h', 2), - ("Input coupling 2", EINCPL, 'h', 2), - ("Source type", ESRCTYP, 'h', 2), - ("Chirp percent", int, 'h', 2), - ("Burst percent", int, 'h', 2), - ("Sweep direction", ESWPDIR, 'h', 2), - ("Sweep mode", ESWPMOD, 'h', 2), - ("Ext sample freq untis", EXAXIS, 'h', 2), - ("Bandwidth units", EXAXIS, 'h', 2), - ("Log span index", int, 'h', 2), - ("Log start index", int, 'h', 2), - ("Sweep rate units", EXAXIS, 'h', 2), - ("Auto gain ref chan", EDEMODCH, 'h', 2), - ("Demod channels", EDEMODCH, 'h', 2), - ("Demod type chan 1", EDEMOD, 'h', 2), - ("Demod type chan 2", EDEMOD, 'h', 2), - ("Source level units", EXAXIS, 'h', 2), - ("Source offset units", EXAXIS, 'h', 2), - ("Trigger level units", EXAXIS, 'h', 2), - ("Capt/thru length units", EXAXIS, 'h', 2), - ("EU label 1", str, 's', 6), - ("EU label 2", str, 's', 6), - ("Auto carrier on/off", bool, 'h', 2), - ("Time average on/off", bool, 'h', 2), - ("Auto/fixed resolution", bool, 'h', 2), - ("Auto gain on/off", bool, 'h', 2), - ("Auto/fixed integrate",bool, 'h', 2), - ("Fast average on/off", bool, 'h', 2), - ("Overload reject on/off", bool, 'h', 2), - ("Chan 1 float/ground", bool, 'h', 2), - ("Chan 2 float/ground", bool, 'h', 2), - ("Time throughput on/off", bool, 'h', 2), - ("Demodulation on/off", bool, 'h', 2), - ("EU/volts chan 1", bool, 'h', 2), - ("EU/volts chan 2", bool, 'h', 2), - ("Manual/auto arm", bool, 'h', 2), - ("Demod preview on/off", bool, 'h', 2), - ("Delete freq on/off", bool, 'h', 2), - ("Lin res Fstart pegged", bool, 'h', 2), - ("Swept Fstart pegged", bool, 'h', 2), - ("Force length chan 1", decode_float, None, 4), - ("Force length chan 2", decode_float, None, 4), - ("Expon time constant 1", decode_float, None, 4), - ("Expon time constant 2", decode_float, None, 4), - ("Sweep time", decode_float, None, 4), - ("Sweep rate", decode_float, None, 4), - ("Sweep resolution", decode_float, None, 4), - ("Sweep integrate time", decode_float, None, 4), - ("Auto gain level", decode_float, None, 4), - ("Auto gain limit", decode_float, None, 4), - ("Source level", decode_float, None, 4), - ("EU value chan 1", decode_float, None, 4), - ("EU value chan 2", decode_float, None, 4), - ("Trigger delay chan 1", decode_float, None, 4), - ("Trigger delay chan 2", decode_float, None, 4), - ("Integrate var thresh", decode_float, None, 4), - ("Capt/thru length", decode_float, None, 4), - ("Frequency span", decode_float, None, 4), - ("Time record length", decode_float, None, 4), - ("Frequency resolution", decode_float, None, 4), - ("Time resolution", decode_float, None, 4), - ("External sample rate", decode_float, None, 4), - ("Sample rate (actual)", decode_float, None, 4), - ("Range channel 1", decode_float, None, 4), - ("Range channel 2", decode_float, None, 4), - ("Preview time", decode_float, None, 4), - ("Trigger level", decode_float, None, 4), - ("Source dc offset", decode_float, None, 4), - ("Fixed sine frequency", decode_float, None, 8), - ("Start frequency", decode_float, None, 8), - ("Center frequency", decode_float, None, 8), - ("Sweep start", decode_float, None, 8), - ("Sweep end", decode_float, None, 8), - ("Carrier frequency", decode_float, None, 8), - ] - -def decode_state(data): - """ - Decode the data (as generated by the HP3562A DSA in response to a - "DSBN" command), and returns a dict (header) - - header is the dictionnary of the header of the dumped data block, - """ - header, idx = decode_header(data, HEADER) - return header - - -def main(): - import sys - import optparse - opt = optparse.OptionParser("A simple tool for tracing a dumped trace") - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, read from stdin') - opt.add_option('-m', '--mode', default='binary', - dest='mode', - help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', - ) - - options, argv = opt.parse_args(sys.argv) - - - if options.filename is None: - print "Can't deal stdin for now..." - sys.exit(1) - #try: - if 1: - header = decode_state(open(options.filename, 'rb').read()) - else: - #except Exception, e: - print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename - print e - sys.exit(1) - - print format_header(header, HEADER, 100) - -if __name__ == "__main__": - main() -
--- a/HP3562A/trace_decoder.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -# -*- coding: utf-8 -*- -""" -trace_decoder -============= - -Module for decoding a trace generated by the HP3562A DSA, using the -GPIB command "DDBN" (Dump Data BiNary). - -This file can be exectued as a python script. Use '-h' for more informations. -""" -import struct -import numpy -from HP3562A import format_header, decode_float, decode_string, decode_header, read_trace -from HP3562A.enum_types import * - - -HEADER = [ ("Display function", EDSP, 'h', 2), - ('Number of elements', int, 'h', 2), - ('Displayed elements', int, 'h', 2), - ('Number of averages', int, 'h', 2), - ('Channel selection', ECH, 'h', 2), - ('Overflow status', EOVR, 'h', 2), - ('Overlap percentage', int, 'h', 2), - ('Domain', EDOM, 'h', 2), - ('Volts peak/rms', EVLT, 'h', 2), - ('Amplitude units', EAMP, 'h', 2), - ('X axis units', EXAXIS, 'h', 2), - ('Auto math label', str, 's', 14), - ('Trace label', str, 's', 22), - ('EU label 1', str, 's', 6), - ('EU label 2', str, 's', 6), - ('Float/Interger', bool, 'h', 2), - ('Complex/Real', bool, 'h', 2), - ('Live/Recalled', bool, 'h', 2), - ('Math result', bool, 'h', 2), - ('Real/Complex input', bool, 'h', 2), - ('Log/Linear data', bool, 'h', 2), - ('Auto math', bool, 'h', 2), - ('Real time status', bool, 'h', 2), - ('Measurement mode', EMEAS, 'h', 2), - ('Window', EWIN, 'h', 2), - ('Demod type channel 1', EDEMOD, 'h', 2), - ('Demod type channel 2', EDEMOD, 'h', 2), - ('Demod active channel 1', bool, 'h', 2), - ('Demod active channel 2', bool, 'h', 2), - ('Average status', EAVG, 'h', 2), - ('Not used', int, 'hh', 4), - ('Samp freq/2 (real)', decode_float, None, 4), - ('Samp freq/2 (imag)', decode_float, None, 4), - ('Not used', decode_float, None, 4), - ('Delta X-axis', decode_float, None, 4), - ('Max range', decode_float, None, 4), - ('Start time value', decode_float, None, 4), - ('Expon wind const 1', decode_float, None, 4), - ('Expon wind const 2', decode_float, None, 4), - ('EU value chan 1', decode_float, None, 4), - ('EU value chan 2', decode_float, None, 4), - ('Trig delay chan 1', decode_float, None, 4), - ('Trig delay chan 2', decode_float, None, 4), - ('Start freq value', decode_float, None, 8), - ('Start data value', decode_float, None, 8), - ] - -def decode_trace(data, idx=0): - """ - Decode the data (as generated by the HP3562A DSA in response to a - "DDBN" command), and returns a couple (header, values). - - header is the dictionnary of the header of the dumped data block, - value is a numpy array holding the trace (vector of float or - complex values). - """ - header, idx = decode_header(data, HEADER, idx) - return header, read_trace(data, idx, header["Number of elements"]) - -def main(): - import sys - import optparse - opt = optparse.OptionParser("A simple tool for tracing a dumped trace") - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, read from stdin') - opt.add_option('-m', '--mode', default='binary', - dest='mode', - help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', - ) - opt.add_option('-d', '--display-header', default=False, - action="store_true", - dest="displayheader", - help="Display the trace header") - opt.add_option('-P', '--noplot-trace', default=True, - action="store_false", - dest="plot", - help="Do not display the plot of the trace") - opt.add_option('-x', '--xmode', default='lin', - dest='xmode', - help='X coordinate mode (may be "lin" [default] or "log")') - opt.add_option('-y', '--ymode', default='lin', - dest='ymode', - help='Y coordinate mode (may be "lin" [default], "log" or "db")') - - options, argv = opt.parse_args(sys.argv) - - - if options.filename is None: - print "Can't deal stdin for now..." - sys.exit(1) - try: - header, data = decode_trace(open(options.filename, 'rb').read()) - except Exception, e: - print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename - print e - sys.exit(1) - - if options.displayheader: - print format_header(header, HEADER, 100) - if options.plot: - f0 = header['Start freq value'] - dx = header['Delta X-axis'] - n = header['Number of elements'] - x = numpy.linspace(f0, f0+dx*n, len(data)) - y = data.copy() - - import pylab - if options.ymode != "lin": - minv = min(y[y>0]) - y[y==0] = minv - y = numpy.log10(y) - if options.ymode == "db": - y = y*10 - pylab.ylabel('db') - pylab.grid() - pylab.plot(x, y) - pylab.xlabel('frequency') - pylab.show() - - -if __name__ == "__main__": - main()
--- a/bin/dump_datablock Mon Feb 04 19:49:48 2008 +0100 +++ b/bin/dump_datablock Tue Feb 05 18:59:29 2008 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/python -from HP3562A.dump_datablock import main +import sys, os +try: + from pygpibtoolkit.HP3562A.dump_datablock import main +except ImportError: + sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) + from pygpibtoolkit.HP3562A.dump_datablock import main main()
--- a/bin/qgpibplotter Mon Feb 04 19:49:48 2008 +0100 +++ b/bin/qgpibplotter Tue Feb 05 18:59:29 2008 +0100 @@ -1,8 +1,8 @@ #!/usr/bin/python import sys, os try: - from plotter.qgpib_plotter import main + from pygpibtoolkit.plotter.qgpib_plotter import main except ImportError: sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) - from plotter.qgpib_plotter import main + from pygpibtoolkit.plotter.qgpib_plotter import main main()
--- a/bin/read_coord Mon Feb 04 19:49:48 2008 +0100 +++ b/bin/read_coord Tue Feb 05 18:59:29 2008 +0100 @@ -2,8 +2,8 @@ import sys, os try: - from HP3562A.coord_decoder import main + from pygpibtoolkit.HP3562A.coord_decoder import main except ImportError: sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) - from HP3562A.coord_decoder import main + from pygpibtoolkit.HP3562A.coord_decoder import main main()
--- a/bin/read_state Mon Feb 04 19:49:48 2008 +0100 +++ b/bin/read_state Tue Feb 05 18:59:29 2008 +0100 @@ -2,8 +2,8 @@ import sys, os try: - from HP3562A.state_decoder import main + from pygpibtoolkit.HP3562A.state_decoder import main except ImportError: sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) - from HP3562A.state_decoder import main + from pygpibtoolkit.HP3562A.state_decoder import main main()
--- a/bin/read_trace Mon Feb 04 19:49:48 2008 +0100 +++ b/bin/read_trace Tue Feb 05 18:59:29 2008 +0100 @@ -1,8 +1,8 @@ #!/usr/bin/python import sys, os try: - from HP3562A.trace_decoder import main + from pygpibtoolkit.HP3562A.trace_decoder import main except ImportError: sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) - from HP3562A.trace_decoder import main + from pygpibtoolkit.HP3562A.trace_decoder import main main()
--- a/plotter/gpib_plotter.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -import sys, os -import time -import gpib - -import gpib -from prologix import GPIB -class GPIBplotter(GPIB): - _replies={ - "OE": "0", - "OH": "0,0,10000,7500", - "OI": "7470A", - "OP": "0,0,10000,7500", - "OO": "0,1,0,0,0,0,0,0", - "OF": "40,40", - "OS": "24", - } - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=5): - super(GPIBplotter, self).__init__(device, baudrate, timeout, address, mode=0) - - def plotStarted(self): - pass - - def load_plot(self, wait_timeout=0): - """ - Make a full plot process. - - 'wait_timeout' is the first timeout. Same semantic as the - 'timeout' property of a serial connection (if set to None, it - will block until the GPIB device actually perform a plot - command.) - """ - res = "" - i=0 - replies = self._replies.copy() - if wait_timeout is not None and not isinstance(wait_timeout, (float, int)): - raise TypeError, "wait_timeout (%s:%s) has wrong type"%(repr(wait_timeout), type(wait_timeout)) - if wait_timeout<0: - raise ValueError - - self._cnx.timeout = wait_timeout - firstloop = True - newdata = False - while i<self._retries: - l = self._cnx.readline().strip() - if firstloop: - self._cnx.timeout = self._timeout - firstloop = False - self.plotStarted() - if l == "": - if i == 0:# > (self._retries/2): - # ie. we just received new stuffs (i is reset in the else block) - for k, v in replies.items(): - # check wether we should reply smthg - eres = res.replace('\n', '').strip() - if eres.endswith(k) or eres.endswith(k+';') or eres.endswith(k+';OE'): - self._cnx.write("%s"%v) - if k == "OS": - replies[k] = "16" - break - self._cnx.write('\r') - #time.sleep(0.1) - i += 1 - else: - if not res: - print "Plotting..." - res += l + '\n' - i = 0 - #time.sleep(0.1) - if res: - print "DONE (received %d characters)"%len(res) - return res - -if __name__ == '__main__': - import optparse - opt = optparse.OptionParser('A simple HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') - opt.add_option('-f', '--filename', default=None, - dest='filename', - help='Output filename. If not set, write to stdout') - opt.add_option('-d', '--device', default='/dev/ttyUSB0', - dest='device', - help='Device of the RS232 connection (default: /dev/ttyUSB0)', - ) - opt.add_option('-a', '--address', default=0, - dest='address', - help='GPIB address of the device', - ) - opt.add_option('-l', '--loop', default=False, - action="store_true", - dest="loop", - help="Continuously wait for new plots. If set, filename must be set and files will be created with _n at the end") - opt.add_option('-v', '--verbose', default=False, - action="store_true", - dest="verbose", - help="Verbose mode",) - - options, argv = opt.parse_args(sys.argv) - - if options.loop and not options.filename: - opt.error('If loop is set, you *must* provide a filename') - - if options.filename: - outf = open(options.filename, "w") - else: - outf = sys.stdout - - - try: - plotter = GPIBplotter(device=options.device, address=int(options.address), - timeout=0.06) - except (gpib.SerialException, gpib.ConnectionError), e: - sys.stderr.write('Connection error:\n\t' + '\n\t'.join([str(x) for x in e.args]) + '\n') - sys.stderr.write('Check your parameters\n') - sys.exit(1) - if options.verbose: - sys.stderr.write('connection established\n') - - loop = True - nloop = 0 - while loop: - plot = plotter.load_plot(wait_timeout=0.1) - if options.verbose: - sys.stderr.write('.') - if plot: - outf.write(plot) - if options.verbose: - sys.stderr.write('\n') - sys.stderr.write('Received a new plot (written to %s)\n'%outf.name) - if not options.loop: - loop = False - else: - nloop += 1 - fname, ext = os.path.splitext(options.filename) - outf = open(fname + "_%d"%nloop + ext, 'w') - - - - -
--- a/plotter/gpib_plotter_mockup.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -import glob -import os -import time -import random - -class GPIBplotter:#(GPIB): - """ - A mockup thet will find in a directory some HPGL files and feed them randomly - """ - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=5): - self._timeout = timeout - self.filenames = glob.glob('examples/*.plt') - self._next = random.randint(10,50) - self._num = 0 - - def plotStarted(self): - pass - - def load_plot(self, wait_timeout=0): - if wait_timeout: - time.sleep(wait_timeout) - self._num += 1 - if self._num > self._next: - ret = open(random.choice(self.filenames)).read() - if len(ret)>0: - self.plotStarted() - self._num = 0 - self._next = random.randint(10,100) - time.sleep(random.randint(1,3)) - return ret - return None - return None - -
--- a/plotter/hpgl_mpl.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -import numpy -import pylab - -from hpgl_parser import HPGLParser - -class HPGLmplPlotter(HPGLParser): - pens = "krbgcmyw" - lines = ['-', ':', '--', '-.', '.', '-', '-', '-'] - def __init__(self, data): - super(HPGLmplPlotter, self).__init__(data) - pylab.show() - - def plot_symbols(self, points): - x, y = points.T - pylab.plot(x, y, 'o') - - def plot_lines(self, points): - x, y = points.T - pylab.plot(x, y, self.pens[self.pen]+self.lines[self.line_type]) - -if __name__ == "__main__": - import sys - data = open(sys.argv[1]).read() - HPGLmplPlotter(data) -
--- a/plotter/hpgl_parser.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,451 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -import numpy - -vsplitter = re.compile('[ ,]') -vextractor = re.compile('(?P<value>[^;\n\r\aA-DF-Za-df-z]*?)(?P<terminator>[;\n\r\a]+)', re.S+re.M) - -class HPGLParser(object): - def __init__(self, data=None): - self.str_terminator = chr(0x03) - self.IN() - if data: - self.parse(data) - - def parse(self, data): - self.data = data - self.idx = 0 - self.IN() - while self.idx<len(data): - while data[self.idx] in [';','\n','\r', '\a', ' ']: - self.idx += 1 - if data[self.idx] == chr(0x03): - self.ESC() - else: - cmd = data[self.idx: self.idx+2].upper() - self.idx += 2 - getattr(self, cmd)() - while self.idx<len(data) and data[self.idx] in [';','\n','\r', '\a', ' ']: - self.idx += 1 - - def user_to_abs(self, pos): - """ - converts a position (x, y) given in user units to absolute - units - """ - return pos #TODO - - def extract_value(self, raw=False): - m = vextractor.match(self.data[self.idx:]) - if m: - val = m.group("value") - self.idx = self.idx + len(val) + len(m.group("terminator")) - val = vsplitter.split(val) - if not raw: - val = [eval(x) for x in val if x.strip()] - return val - return [] - - def extract_string(self): - idx2 = self.data.find(self.str_terminator, self.idx) - val = self.data[self.idx: idx2] - self.idx = idx2+1 - return val - - def polyline(self, values): - if (len(values)%2) == 1: - # this is a syntax error in the HPGL data - values = values[:-1] - values = numpy.array(values).reshape(-1, 2) - # TODO use scaling - # do plots - values = numpy.r_[[self.pos], values] #add self.pos as 1st value - if self.plot_mode == 'relative': - values = values.cumsum(axis=0) - if self.symbol_mode: - self.plot_symbols(values[1:]) - if self.pen_state == "down": - self.plot_lines(values) - self.pos = values[-1] - - def plot_string(self, s): - pass - - def get_window_for_paper(self): - return 0,100,0,100 - def get_scaling_points_for_paper(self): - return 0,100,0,100 - - # effective plot methods; to be defined in subclasses (backends) - def plot_symbols(self, points): - pass - def plot_lines(self, points): - pass - - # HPGL-related methods - def ESC(self): - #print "ESC" - self.idx += 1 - - def OE(self): - """ Output Error """ - pass - - def EC(self): - """? what's this """ - values = self.extract_value() - - def OO(self): - """idem""" - pass - - def OP(self): - """idem""" - pass - - def VS(self): - """idem""" - pass - - - def DF(self): - """ Default """ - self.pen = 0 - self.pen_width = 1 # 1/300 inch - self.pos = 0,0 - self.char_set = "std" - self.plot_mode = 'absolute' - self.char_direction = 1,0 - self.line_type = 0 # 'solid' - self.pattern_len = 4 #percentage - self.window = self.get_window_for_paper() - self.char_size = 0.75, 1.5 #percentages - self.symbol_mode = False - self.tick_len = 0.5, 0.5 # % - self.std_char_set = 0 - self.alt_char_set = 0 - self.selected_char_set = 0 - self.char_slant = 0 # degrees - self.scale = None - self.str_terminator = chr(0x03) - self.chord_ang = 5 #degrees - self.fill_type = 'bidirectionnal', 1 - self.fill_distance = 1 # % - self.fill_slant = 0 # degrees - self.pen_thickness = 0.3 # mm - - def IN(self): - """ Initialize """ - self.DF() - self.pen_state = "up" - self.rotation = 0 #degrees - self.scaling_points = self.get_scaling_points_for_paper() - - def IP(self): - """ Input Scale Point""" - values = self.extract_value() - if len(values) == 0: - self.scaling_points = self.get_scaling_points_for_paper() - elif len(values) == 2: - p1x, p1y, p2x, p2y = self.scaling_points - dx, dy = p2x-p1x, p2y-p1y - x, y = values - self.scaling = (x, y, x+dx, y+dy) - elif len(values) == 4: - self.values = tuple(values) - - def SC(self): - """ Scale """ - values = self.extract_value() - if len(values) == 4: - self.scale = tuple(values) - else: - self.scale = None - - def IW(self): - values = self.extract_value() - if len(values) == 0: - self.window = self.get_window_for_paper() - elif len(value) == 4: - self.window = tuple(values) - - def RO(self): - values = self.extract_value() - if len(values) == 0: - self.rotation = 0 - elif len(values) == 1: - self.rotation = values[0] - - def PG(self): - pass - - def PU(self): - """ Pen Up""" - self.pen_state = "up" - values = self.extract_value() - self.polyline(values) - - def PD(self): - """ Pen Down """ - self.pen_state = "down" - values = self.extract_value() - self.polyline(values) - - def PA(self): - """ Plot Absolute """ - self.plot_mode = "absolute" - values = self.extract_value() - self.polyline(values) - - def PR(self): - """ Plot Relative """ - self.plot_mode = "relative" - values = self.extract_value() - self.polyline(values) - - def AA(self): - """ Arc Absolute """ - values = self.extract_value() - if len(values) in [3, 4]: - x, y, qc = values[:3] - if len(values)==4: - qd = values[-1] - else: - qd = self.chord_ang - # TODO : plot arc - print "plotting an arc" - def AR(self): - """ Arc Relative """ - values = self.extract_value() - # TODO - - def CI(self): - """ Circle Plot""" - values = self.extract_value() - # TODO - - def EA(self): - """Edge Rectangle Absolute""" - values = self.extract_value() - # TODO - - def ER(self): - """Edge Rectangle Relative""" - values = self.extract_value() - # TODO - - def EW(self): - """ Edge Wedge """ - values = self.extract_value() - # TODO - - def RA(self): - """ Fill Rectangle Absolute """ - values = self.extract_value() - # TODO - - def RR(self): - """ Fill Rectangle Relative """ - values = self.extract_value() - # TODO - - def WG(self): - """ Fill Wedge """ - values = self.extract_value() - # TODO - - def FT(self): - """ Fill Type """ - values = self.extract_value() - # TODO - - def LT(self): - """ Line Type """ - values = self.extract_value() - - if len(values)==0: - self.line_type = 0 #'solid' - else: - self.line_type = values[0] - if len(values)>1: - self.pattern_len = values[1] - - def PW(self): - """ Pen Width """ - values = self.extract_value() - if len(values) == 1: - self.pen_thickness = values[0] - - def SM(self): - """ Symbol Mode """ - values = self.extract_value() - if len(values) == 0: - self.symbol_mode = False - elif len(values) == 1: - self.symbol_mode = values[0] - - def SP(self): - """ Select Pen """ - values = self.extract_value() - if len(values) == 1: - self.pen = values[0] - - def TL(self): - """ Tick Len """ - values = self.extract_value() - # TODO - - def XT(self): - """ X-axis Tick """ - # TODO - print "X ticks" - - def YT(self): - """ X-axis Tick """ - # TODO - print "Y ticks" - - def PT(self): - """ Pen Thickness Select """ - values = self.extract_value() - if len(values) == 0: - self.pen_thickness = 0.3 - elif len(values) == 1: - self.pen_thickness = values[0] - - def CS(self): - """ Standard Character Set """ - values = self.extract_value() - if len(values) == 0: - self.std_char_set = 0 - elif len(values) == 1: - self.std_char_set = values[0] - - def CA(self): - """ Alternate Character Set """ - values = self.extract_value() - if len(values) == 0: - self.alt_char_set = 0 - elif len(values) == 1: - self.alt_char_set = values[0] - - def SS(self): - """ Select Standard Character Set """ - self.char_set = "std" - - def SA(self): - """ Select Alternate Character Set """ - self.char_set = "alt" - - def DT(self): - """ Define Label Terminator """ - values = self.extract_value(raw=True) - if len(values) == 0: - self.str_terminator = chr(0x03) - elif len(values) == 1: - self.str_terminator = values[0] - - def LB(self): - """ Character Plot """ - values = self.extract_string() - self.plot_string(values) - x, y = self.pos - values = values.split('\n') - if self.char_size == "absolute": - x += len(values[-1]) * self.char_width *1016/2.54 - y += (len(values)-1) * self.char_height *1016/2.54 - else: - x0, x1, y0, y1 = self.scale - dx = x1-x0 - dy = y1-y0 - x += len(values[-1]) * self.char_width / 100.0 * dx - y += (len(values)-1) * self.char_height / 100.0 * dy - - #print "LB pos[%s] %s -> %s"%(repr(values), self.pos, (x,y)) - self.pos = [x, y] - - def get_char_size(self): - if self.char_size == "absolute": - x = self.char_width *1016/2.54 - y = self.char_height *1016/2.54 - else: - x0, x1, y0, y1 = self.scale - dx = x1-x0 - dy = y1-y0 - x = self.char_width / 100.0 * dx - y = self.char_height / 100.0 * dy - return x, y - - def DI(self): - """ Absolute Direction """ - values = self.extract_value() - if len(values) == 0: - self.char_direction = 1.0, 0.0 - elif len(values) == 2: - self.char_direction = values - - def DR(self): - """ Relative Direction """ - values = self.extract_value() - if len(values) == 0: - self.char_direction = 1.0, 0.0 - elif len(values) == 2: - # TODO : compute as percentages - self.char_direction = values - - def CP(self): - """ Character Plot """ - values = self.extract_value() - # TODO - if len(values) == 0: - values = 0, 1 - x, y = self.pos - if self.char_size == "absolute": - x += values[0] * self.char_width *1016/2.54 - y += values[1] * self.char_height *1016/2.54 - else: - x0, x1, y0, y1 = self.scale - dx = x1-x0 - dy = y1-y0 - x += values[0] * self.char_width / 100.0 * dx - y += values[1] * self.char_height / 100.0 * dy - #print "CB pos[%s] %s -> %s"%(repr(values), self.pos, (x,y)) - self.pos = [x, y] - - def SI(self): - """ Set Absolute Character Size """ - values = self.extract_value() - self.char_size = "absolute" - if len(values) == 0: - self.char_width = 0.1879 # cm - self.char_height = 0.2690 # cm - elif len(values) == 2: - self.char_width, self.char_height = values - - def SR(self): - """ Set Relative Character Size """ - values = self.extract_value() - self.char_size = "relative" - if len(values) == 0: - self.char_width = 0.75 # percentage - self.char_height = 1.5 # id - elif len(values) == 2: - self.char_width, self.char_height = values - - def SL(self): - """ Character Slant """ - values = self.extract_value() - # TODO - - def UC(self): - """ User Defined Character """ - values = self.extract_value() - # TODO - -if __name__ == '__main__': - import sys - data = open(sys.argv[1]).read() - - p = HPGLParser(data) -
--- a/plotter/hpgl_plotter_rc.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1318 +0,0 @@ -# -*- coding: utf-8 -*- - -# Resource object code -# -# Created: sam. janv. 26 11:14:20 2008 -# by: The Resource Compiler for PyQt (Qt v4.3.2) -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore - -qt_resource_data = "\ -\x00\x00\x14\x97\ -\x3c\ -\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ -\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ -\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ -\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ -\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ -\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ -\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ -\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ -\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ -\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ -\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ -\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ -\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ -\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ -\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ -\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ -\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ -\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ -\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ -\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ -\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ -\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ -\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ -\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ -\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x35\ -\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\x38\ -\x2e\x32\x35\x32\x35\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\x73\x76\ -\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ -\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\x0a\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\x73\x69\ -\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\x20\x73\ -\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\x65\x3d\ -\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\x6c\x65\ -\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\x2f\x69\ -\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ -\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\x5f\x72\ -\x65\x64\x5f\x6f\x66\x66\x2e\x73\x76\x67\x22\x0a\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\x75\x74\x5f\x65\ -\x78\x74\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\x67\x2e\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\x74\x2e\x73\x76\ -\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\ -\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x3e\x0a\x20\x20\ -\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x64\ -\x65\x66\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\ -\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\ -\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\ -\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\x20\x20\x20\ -\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\ -\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\ -\x70\x34\x31\x31\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ -\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ -\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\ -\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\ -\x31\x32\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\ -\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\ -\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\ -\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\ -\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ -\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\ -\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\ -\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\ -\x74\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\ -\x64\x3d\x22\x73\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\ -\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\ -\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x61\x61\x30\x30\x30\x30\x3b\x73\ -\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\ -\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ -\x22\x73\x74\x6f\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\ -\x20\x20\x3c\x2f\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\ -\x6e\x74\x3e\x0a\x20\x20\x20\x20\x3c\x72\x61\x64\x69\x61\x6c\x47\ -\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\ -\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\ -\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x61\x64\x69\ -\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x34\x32\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x63\x78\x3d\x22\x33\x32\x37\x2e\x34\ -\x31\x39\x32\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x63\x79\x3d\ -\x22\x33\x34\x2e\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x66\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\x32\x35\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x66\x79\x3d\x22\x33\x34\x2e\ -\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x72\ -\x3d\x22\x31\x33\x30\x2e\x31\x33\x32\x34\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\ -\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ -\x74\x54\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\ -\x69\x78\x28\x32\x2e\x35\x36\x39\x35\x37\x30\x37\x65\x2d\x37\x2c\ -\x2d\x33\x2e\x32\x34\x36\x39\x38\x35\x38\x2c\x32\x2e\x32\x39\x36\ -\x32\x34\x35\x2c\x36\x2e\x38\x37\x30\x38\x39\x36\x39\x65\x2d\x36\ -\x2c\x33\x36\x36\x2e\x39\x31\x30\x34\x39\x2c\x31\x35\x30\x34\x2e\ -\x31\x38\x33\x29\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\ -\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\ -\x6c\x65\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\ -\x22\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ -\x34\x31\x31\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ -\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\ -\x31\x32\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\ -\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\ -\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\ -\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\ -\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\ -\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\ -\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\ -\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\ -\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\ -\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\ -\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ -\x74\x55\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\ -\x65\x4f\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\ -\x31\x3d\x22\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\ -\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\ -\x38\x38\x2e\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x79\x32\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\ -\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ -\x28\x31\x2e\x32\x34\x35\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\ -\x2e\x32\x36\x31\x35\x38\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\ -\x36\x38\x2c\x2d\x39\x30\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\ -\x2f\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\ -\x65\x77\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\ -\x22\x0a\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\ -\x3d\x22\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\ -\x62\x6f\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\ -\x36\x36\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\ -\x72\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ -\x65\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ -\x65\x73\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\ -\x30\x2e\x39\x36\x31\x36\x34\x36\x31\x31\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x32\ -\x36\x2e\x33\x38\x33\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x36\x39\x2e\x37\x31\ -\x35\x34\x39\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\ -\x70\x65\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\ -\x73\x3d\x22\x70\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\ -\x65\x72\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ -\x2d\x77\x69\x64\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\ -\x77\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\ -\x64\x6f\x77\x2d\x78\x3d\x22\x31\x31\x22\x0a\x20\x20\x20\x20\x20\ -\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ -\x79\x3d\x22\x31\x33\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\ -\x61\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\ -\x65\x74\x61\x64\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\ -\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\ -\x63\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\ -\x74\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\ -\x2f\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\ -\x75\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\ -\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\ -\x65\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\ -\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\ -\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\ -\x0a\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\ -\x20\x3c\x67\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\ -\x65\x3a\x6c\x61\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ -\x3a\x67\x72\x6f\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\ -\x72\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\ -\x72\x31\x22\x0a\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\ -\x72\x6d\x3d\x22\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\ -\x37\x35\x2e\x39\x35\x38\x34\x37\x2c\x2d\x31\x37\x31\x2e\x37\x39\ -\x38\x39\x34\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\ -\x33\x39\x37\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\ -\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\ -\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ -\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x33\x31\x33\ -\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\ -\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\ -\x75\x72\x6c\x28\x23\x72\x61\x64\x69\x61\x6c\x47\x72\x61\x64\x69\ -\x65\x6e\x74\x33\x31\x34\x32\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\ -\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\ -\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x77\x69\x64\x74\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\ -\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\ -\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\ -\x30\x36\x36\x36\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\ -\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\ -\x69\x74\x79\x3a\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\ -\x65\x63\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\ -\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\ -\x3a\x23\x61\x32\x30\x30\x30\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\ -\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\ -\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x77\x69\x64\x74\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\ -\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\ -\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\ -\x30\x36\x36\x36\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\ -\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\ -\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\ -\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x30\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\ -\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\ -\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\ -\x32\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\ -\x30\x2e\x39\x33\x39\x37\x34\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\ -\x3c\x72\x65\x63\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\ -\x31\x37\x36\x2e\x33\x38\x37\x34\x34\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x78\x3d\x22\x31\x38\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\ -\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x77\x69\x64\x74\x68\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\ -\x32\x31\x36\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\ -\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\ -\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\ -\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\ -\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\ -\x23\x39\x35\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\ -\x69\x64\x74\x68\x3a\x39\x2e\x31\x37\x37\x30\x30\x30\x30\x35\x3b\ -\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\ -\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\ -\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\ -\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\ -\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\ -\x3a\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\ -\x69\x6c\x6c\x3a\x75\x72\x6c\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\ -\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\ -\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\ -\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\ -\x72\x6f\x6b\x65\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\ -\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\ -\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\ -\x22\x4d\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\ -\x2e\x39\x33\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\ -\x35\x2c\x33\x35\x32\x2e\x35\x36\x32\x35\x20\x43\x20\x31\x39\x30\ -\x2e\x39\x33\x37\x35\x32\x2c\x33\x35\x32\x2e\x37\x39\x39\x38\x32\ -\x20\x31\x39\x36\x2e\x38\x33\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\ -\x33\x37\x35\x20\x32\x30\x32\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\ -\x32\x2e\x39\x33\x37\x35\x20\x43\x20\x33\x31\x32\x2e\x38\x30\x30\ -\x31\x34\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\x34\x30\x36\x2e\ -\x39\x35\x30\x34\x39\x2c\x33\x31\x31\x2e\x36\x31\x30\x34\x34\x20\ -\x34\x34\x35\x2e\x30\x36\x32\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\ -\x4c\x20\x34\x34\x35\x2e\x30\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\ -\x33\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\ -\x31\x38\x30\x2e\x39\x33\x37\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x34\ -\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\ -\x79\x70\x65\x3d\x22\x61\x72\x63\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\ -\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\ -\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\ -\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\ -\x6f\x6b\x65\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\ -\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\ -\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\ -\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\ -\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\ -\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\ -\x34\x36\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\ -\x6f\x64\x69\x3a\x63\x78\x3d\x22\x33\x30\x33\x2e\x36\x34\x36\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\ -\x3a\x63\x79\x3d\x22\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ -\x72\x78\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\ -\x64\x69\x70\x6f\x64\x69\x3a\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\ -\x31\x38\x35\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\ -\x4d\x20\x33\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\ -\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\ -\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\ -\x2c\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\x20\x37\ -\x2e\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\x20\x20\ -\x33\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\ -\x34\x20\x7a\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\ -\x2f\x73\x76\x67\x3e\x0a\ -\x00\x00\x14\x96\ -\x3c\ -\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ -\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ -\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ -\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ -\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ -\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ -\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ -\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ -\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ -\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ -\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ -\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ -\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ -\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ -\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ -\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ -\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ -\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ -\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ -\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ -\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ -\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ -\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ -\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ -\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x34\ -\x31\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\ -\x38\x2e\x32\x35\x32\x34\x31\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ -\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ -\x69\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\ -\x0a\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\ -\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\ -\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\ -\x65\x3d\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\ -\x6c\x65\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\ -\x2f\x69\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\ -\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\ -\x5f\x67\x72\x65\x65\x6e\x2e\x73\x76\x67\x22\x0a\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\x75\x74\x5f\x65\ -\x78\x74\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\x67\x2e\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\x74\x2e\x73\x76\ -\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\ -\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x3e\x0a\x20\x20\ -\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x64\ -\x65\x66\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\ -\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\ -\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\ -\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\x20\x20\x20\ -\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\ -\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\ -\x70\x34\x31\x31\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ -\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ -\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\ -\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\ -\x31\x32\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\ -\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\ -\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\ -\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\ -\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ -\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\ -\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\ -\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\ -\x74\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\ -\x64\x3d\x22\x73\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\ -\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\ -\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x30\x30\x66\x66\x30\x30\x3b\x73\ -\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\ -\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ -\x22\x73\x74\x6f\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\ -\x20\x20\x3c\x2f\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\ -\x6e\x74\x3e\x0a\x20\x20\x20\x20\x3c\x72\x61\x64\x69\x61\x6c\x47\ -\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\ -\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\ -\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x61\x64\x69\ -\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x34\x32\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x63\x78\x3d\x22\x33\x32\x37\x2e\x34\ -\x31\x39\x32\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x63\x79\x3d\ -\x22\x33\x34\x2e\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x66\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\x32\x35\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x66\x79\x3d\x22\x33\x34\x2e\ -\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x72\ -\x3d\x22\x31\x33\x30\x2e\x31\x33\x32\x34\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\ -\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ -\x74\x54\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\ -\x69\x78\x28\x32\x2e\x35\x36\x39\x35\x37\x30\x37\x65\x2d\x37\x2c\ -\x2d\x33\x2e\x32\x34\x36\x39\x38\x35\x38\x2c\x32\x2e\x32\x39\x36\ -\x32\x34\x35\x2c\x36\x2e\x38\x37\x30\x38\x39\x36\x39\x65\x2d\x36\ -\x2c\x33\x36\x36\x2e\x39\x31\x30\x34\x39\x2c\x31\x35\x30\x34\x2e\ -\x31\x38\x33\x29\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\ -\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\ -\x6c\x65\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\ -\x22\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ -\x34\x31\x31\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ -\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\ -\x31\x32\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\ -\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\ -\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\ -\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\ -\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\ -\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\ -\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\ -\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\ -\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\ -\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\ -\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ -\x74\x55\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\ -\x65\x4f\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\ -\x31\x3d\x22\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\ -\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\ -\x38\x38\x2e\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x79\x32\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\ -\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ -\x28\x31\x2e\x32\x34\x35\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\ -\x2e\x32\x36\x31\x35\x38\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\ -\x36\x38\x2c\x2d\x39\x30\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\ -\x2f\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\ -\x65\x77\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\ -\x22\x0a\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\ -\x3d\x22\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\ -\x62\x6f\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\ -\x36\x36\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\ -\x72\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ -\x65\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ -\x65\x73\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\ -\x30\x2e\x34\x38\x30\x38\x32\x33\x30\x36\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x37\ -\x32\x2e\x30\x34\x37\x32\x34\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x32\x30\x37\x2e\x31\ -\x32\x31\x33\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\ -\x70\x65\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\ -\x73\x3d\x22\x70\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\ -\x65\x72\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ -\x2d\x77\x69\x64\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\ -\x77\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\ -\x64\x6f\x77\x2d\x78\x3d\x22\x32\x35\x22\x0a\x20\x20\x20\x20\x20\ -\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ -\x79\x3d\x22\x34\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\ -\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\ -\x74\x61\x64\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\ -\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\ -\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\ -\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\ -\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\ -\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\ -\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\ -\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\ -\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\x3e\ -\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\ -\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\x20\ -\x3c\x67\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ -\x3a\x6c\x61\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\x31\ -\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\ -\x67\x72\x6f\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\x72\ -\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\x72\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\ -\x6d\x3d\x22\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\x37\ -\x35\x2e\x39\x35\x38\x35\x32\x2c\x2d\x31\x37\x31\x2e\x37\x39\x38\ -\x39\x39\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x30\x30\ -\x66\x66\x30\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ -\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ -\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\ -\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ -\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ -\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ -\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\ -\x63\x74\x32\x31\x36\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\ -\x69\x64\x74\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ -\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\ -\x39\x37\x34\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\ -\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x37\x36\x2e\ -\x33\x38\x37\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\ -\x22\x31\x38\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x36\x39\x2e\x30\ -\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ -\x68\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x32\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\ -\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\ -\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\ -\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\x63\ -\x34\x30\x62\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\ -\x3a\x39\x2e\x31\x37\x36\x38\x39\x38\x39\x36\x3b\x73\x74\x72\x6f\ -\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\ -\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\ -\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\ -\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x20\ -\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\x39\x37\x34\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\ -\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\ -\x69\x67\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x32\ -\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x33\x31\x33\x34\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\ -\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x75\x72\x6c\x28\ -\x23\x72\x61\x64\x69\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\x33\ -\x31\x34\x32\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ -\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ -\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\ -\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ -\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ -\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ -\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ -\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ -\x6c\x6c\x3a\x75\x72\x6c\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\ -\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\x6c\ -\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\ -\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\ -\x6f\x6b\x65\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\ -\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\ -\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\ -\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\ -\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\ -\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\ -\x4d\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\ -\x39\x33\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\ -\x2c\x33\x35\x32\x2e\x35\x36\x32\x35\x20\x43\x20\x31\x39\x30\x2e\ -\x39\x33\x37\x35\x32\x2c\x33\x35\x32\x2e\x37\x39\x39\x38\x32\x20\ -\x31\x39\x36\x2e\x38\x33\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\x33\ -\x37\x35\x20\x32\x30\x32\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\x32\ -\x2e\x39\x33\x37\x35\x20\x43\x20\x33\x31\x32\x2e\x38\x30\x30\x31\ -\x34\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\x34\x30\x36\x2e\x39\ -\x35\x30\x34\x39\x2c\x33\x31\x31\x2e\x36\x31\x30\x34\x34\x20\x34\ -\x34\x35\x2e\x30\x36\x32\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\x4c\ -\x20\x34\x34\x35\x2e\x30\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\x33\ -\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\ -\x38\x30\x2e\x39\x33\x37\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x34\x22\ -\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\x79\ -\x70\x65\x3d\x22\x61\x72\x63\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\ -\x3b\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\ -\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\ -\x6b\x65\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\ -\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\ -\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\ -\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\ -\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\ -\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\ -\x36\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\ -\x64\x69\x3a\x63\x78\x3d\x22\x33\x30\x33\x2e\x36\x34\x36\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ -\x63\x79\x3d\x22\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x72\ -\x78\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\ -\x69\x70\x6f\x64\x69\x3a\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\x31\ -\x38\x35\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\ -\x20\x33\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\ -\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\x33\ -\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\x2c\ -\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\ -\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\ -\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\ -\x20\x7a\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\ -\x73\x76\x67\x3e\x0a\ -\x00\x00\x10\xa0\ -\x3c\ -\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ -\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ -\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ -\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ -\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ -\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ -\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ -\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ -\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ -\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ -\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ -\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ -\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ -\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ -\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ -\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ -\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ -\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ -\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ -\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ -\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ -\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ -\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ -\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ -\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x34\ -\x31\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\ -\x38\x2e\x32\x35\x32\x34\x31\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ -\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ -\x69\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\ -\x0a\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\ -\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\ -\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\ -\x65\x3d\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\ -\x6c\x65\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\ -\x2f\x69\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\ -\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\ -\x5f\x67\x72\x65\x65\x6e\x5f\x6f\x66\x66\x2e\x73\x76\x67\x22\x0a\ -\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\ -\x75\x74\x5f\x65\x78\x74\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\ -\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\ -\x74\x2e\x73\x76\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\ -\x20\x20\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\ -\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\ -\x64\x3d\x22\x64\x65\x66\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\ -\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\ -\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\ -\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\ -\x63\x6f\x6c\x6f\x72\x3a\x23\x61\x63\x61\x63\x61\x63\x3b\x73\x74\ -\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x2e\x36\x38\x37\ -\x38\x33\x30\x36\x39\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\x31\x31\ -\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\ -\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\ -\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\ -\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\ -\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\ -\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\x31\x32\x31\x22\ -\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\x6e\x65\x61\x72\ -\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\ -\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\ -\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\x3e\x0a\x20\x20\ -\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\ -\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\ -\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\ -\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\ -\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\ -\x6f\x72\x3a\x23\x30\x30\x66\x66\x30\x30\x3b\x73\x74\x6f\x70\x2d\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\ -\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\ -\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\ -\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\ -\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ -\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\x22\x61\x6c\x77\ -\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x6c\x69\x6e\ -\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\x65\x61\x72\x47\ -\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\ -\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\ -\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\x32\x36\x30\ -\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\ -\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\x36\x35\x39\ -\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\x3d\x22\x33\ -\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\x72\x61\x6e\x73\x66\x6f\ -\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\x31\x2e\x32\x34\x35\ -\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\x2e\x32\x36\x31\x35\x38\ -\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\x36\x38\x2c\x2d\x39\x30\ -\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\x2f\x3e\x0a\x20\x20\x3c\ -\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\x73\x6f\x64\x69\x70\x6f\ -\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\x0a\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\x22\x0a\x20\x20\x20\x20\ -\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x66\x66\x66\ -\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\ -\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\x36\x36\x36\x36\x22\x0a\ -\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x6f\x70\x61\x63\x69\ -\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x6f\x70\x61\x63\x69\ -\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x73\x68\x61\x64\x6f\ -\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ -\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\x30\x2e\x34\x38\x30\x38\ -\x32\x33\x30\x36\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ -\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x37\x32\x2e\x30\x34\x37\x32\ -\x34\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ -\x3a\x63\x79\x3d\x22\x2d\x36\x34\x2e\x37\x39\x33\x36\x39\x36\x22\ -\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x64\ -\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\x73\x3d\x22\x70\ -\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ -\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\x65\x72\x3d\x22\ -\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x77\x69\x64\ -\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ -\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x68\x65\ -\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\x20\x20\x20\x20\ -\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ -\x78\x3d\x22\x32\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x79\x3d\x22\x32\ -\x31\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\x64\x61\x74\ -\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\x74\x61\x64\ -\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\ -\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\x63\x3a\x57\ -\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\ -\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x69\x6d\ -\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\x64\x63\x3a\ -\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\x72\x63\x65\ -\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ -\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\x2f\x53\x74\ -\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\ -\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\x3e\x0a\x20\x20\ -\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x3c\ -\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\x20\x3c\x67\x0a\ -\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6c\x61\ -\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\x31\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x67\x72\x6f\ -\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\x72\x22\x0a\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\ -\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\ -\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\x37\x35\x2e\x39\ -\x35\x38\x35\x32\x2c\x2d\x31\x37\x31\x2e\x37\x39\x38\x39\x39\x29\ -\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\ -\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x30\x30\x39\x39\x30\ -\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\ -\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\ -\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\x66\x66\x66\ -\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x30\ -\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\ -\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\ -\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x2c\x20\x30\ -\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\ -\x31\x36\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ -\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x35\x39\ -\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\ -\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\x39\x37\x34\ -\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x37\x36\x2e\x33\x38\x37\ -\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\ -\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\ -\x32\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x32\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\ -\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\ -\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\ -\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\ -\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\x38\x33\x30\x34\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x39\x2e\ -\x31\x37\x36\x38\x39\x38\x39\x36\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\ -\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\ -\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\ -\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x20\x2f\x3e\x0a\ -\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x75\x72\x6c\ -\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ -\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\ -\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\ -\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x6e\x6f\ -\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\ -\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\ -\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\ -\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x31\x38\x35\x2e\ -\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\x35\x20\x4c\ -\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x33\x35\x32\x2e\x35\ -\x36\x32\x35\x20\x43\x20\x31\x39\x30\x2e\x39\x33\x37\x35\x32\x2c\ -\x33\x35\x32\x2e\x37\x39\x39\x38\x32\x20\x31\x39\x36\x2e\x38\x33\ -\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\x32\x30\x32\ -\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\ -\x43\x20\x33\x31\x32\x2e\x38\x30\x30\x31\x34\x2c\x33\x35\x32\x2e\ -\x39\x33\x37\x35\x20\x34\x30\x36\x2e\x39\x35\x30\x34\x39\x2c\x33\ -\x31\x31\x2e\x36\x31\x30\x34\x34\x20\x34\x34\x35\x2e\x30\x36\x32\ -\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\x4c\x20\x34\x34\x35\x2e\x30\ -\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\x35\x20\x4c\x20\x31\ -\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\ -\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ -\x22\x70\x61\x74\x68\x33\x31\x34\x34\x22\x20\x2f\x3e\x0a\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\x20\x20\x73\ -\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\x79\x70\x65\x3d\x22\x61\x72\ -\x63\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\ -\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\ -\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ -\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ -\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\ -\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ -\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\ -\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\ -\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\ -\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x6e\x6f\x6e\ -\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\ -\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\ -\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ -\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x36\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x63\x78\x3d\ -\x22\x33\x30\x33\x2e\x36\x34\x36\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x63\x79\x3d\x22\x31\x32\ -\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x72\x78\x3d\x22\x30\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ -\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\x31\x38\x35\x33\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x33\x30\x33\x2e\x36\ -\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\ -\x20\x37\x2e\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\ -\x20\x20\x33\x30\x33\x2e\x36\x34\x36\x2c\x31\x32\x39\x2e\x39\x38\ -\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\ -\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\ -\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x7a\x22\x20\x2f\x3e\ -\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ -\x00\x00\x14\x96\ -\x3c\ -\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ -\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ -\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ -\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ -\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ -\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ -\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ -\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ -\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ -\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ -\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ -\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ -\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ -\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ -\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ -\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ -\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ -\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ -\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ -\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ -\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ -\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ -\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ -\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ -\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ -\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ -\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ -\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ -\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x34\ -\x31\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\ -\x38\x2e\x32\x35\x32\x34\x31\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ -\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ -\x69\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\ -\x0a\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\ -\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\ -\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\ -\x65\x3d\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\ -\x6c\x65\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\ -\x2f\x69\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\ -\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\ -\x5f\x72\x65\x64\x2e\x73\x76\x67\x22\x0a\x20\x20\x20\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\x75\x74\x5f\x65\x78\x74\ -\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\x67\x2e\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\x74\x2e\x73\x76\x67\x2e\ -\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\x65\x72\ -\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x3e\x0a\x20\x20\x3c\x64\ -\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x64\x65\x66\ -\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\ -\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\ -\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\ -\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\ -\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ -\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\ -\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\ -\x31\x31\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\ -\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\ -\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\ -\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\ -\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\x31\x32\ -\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\x6e\x65\ -\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\x20\x20\ -\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\ -\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\x3e\x0a\ -\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\ -\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\ -\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\ -\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ -\x22\x73\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\x20\x20\ -\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\ -\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x30\x30\x30\x30\x3b\x73\x74\x6f\ -\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\ -\x74\x6f\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\ -\x3c\x2f\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ -\x3e\x0a\x20\x20\x20\x20\x3c\x72\x61\x64\x69\x61\x6c\x47\x72\x61\ -\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x6b\ -\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\x22\x61\ -\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x6c\ -\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\x65\x61\ -\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x61\x64\x69\x61\x6c\ -\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x34\x32\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x63\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\ -\x32\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x63\x79\x3d\x22\x33\ -\x34\x2e\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x66\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\x32\x35\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x66\x79\x3d\x22\x33\x34\x2e\x31\x30\ -\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x72\x3d\x22\ -\x31\x33\x30\x2e\x31\x33\x32\x34\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\x3d\x22\ -\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\ -\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ -\x28\x32\x2e\x35\x36\x39\x35\x37\x30\x37\x65\x2d\x37\x2c\x2d\x33\ -\x2e\x32\x34\x36\x39\x38\x35\x38\x2c\x32\x2e\x32\x39\x36\x32\x34\ -\x35\x2c\x36\x2e\x38\x37\x30\x38\x39\x36\x39\x65\x2d\x36\x2c\x33\ -\x36\x36\x2e\x39\x31\x30\x34\x39\x2c\x31\x35\x30\x34\x2e\x31\x38\ -\x33\x29\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\ -\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\ -\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\ -\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\ -\x31\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\ -\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\ -\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\x32\x36\ -\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\x36\x35\ -\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\x3d\x22\ -\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\x3d\ -\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\x22\ -\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\ -\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\ -\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\ -\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\ -\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\ -\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\ -\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\ -\x22\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\ -\x2e\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\ -\x32\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\x72\x61\ -\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\x31\ -\x2e\x32\x34\x35\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\x2e\x32\ -\x36\x31\x35\x38\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\x36\x38\ -\x2c\x2d\x39\x30\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\x2f\x3e\ -\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\x73\x6f\ -\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\ -\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\x22\x0a\ -\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\x3d\x22\ -\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\ -\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\x36\x36\ -\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x6f\ -\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x6f\ -\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\x20\x20\ -\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x73\ -\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\x30\x2e\ -\x39\x36\x31\x36\x34\x36\x31\x31\x22\x0a\x20\x20\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x32\x36\x2e\ -\x33\x38\x33\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ -\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x38\x39\x2e\x38\x36\x32\x38\ -\x36\x39\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\ -\x65\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\x73\ -\x3d\x22\x70\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ -\x61\x70\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\x65\ -\x72\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\x20\ -\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ -\x77\x69\x64\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\x20\ -\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ -\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\x20\ -\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\ -\x6f\x77\x2d\x78\x3d\x22\x31\x31\x22\x0a\x20\x20\x20\x20\x20\x69\ -\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x79\ -\x3d\x22\x31\x33\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\ -\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\ -\x74\x61\x64\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\ -\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\ -\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\ -\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\ -\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\ -\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\ -\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\ -\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\ -\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\x3e\ -\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\ -\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\x20\ -\x3c\x67\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ -\x3a\x6c\x61\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\x31\ -\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\ -\x67\x72\x6f\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\x72\ -\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\x72\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\ -\x6d\x3d\x22\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\x37\ -\x35\x2e\x39\x35\x38\x35\x32\x2c\x2d\x31\x37\x31\x2e\x37\x39\x38\ -\x39\x39\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x66\x66\ -\x30\x30\x30\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ -\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ -\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\ -\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ -\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ -\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ -\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ -\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\ -\x63\x74\x32\x31\x36\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\ -\x69\x64\x74\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ -\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\ -\x39\x37\x34\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\ -\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x37\x36\x2e\ -\x33\x38\x37\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\ -\x22\x31\x38\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x36\x39\x2e\x30\ -\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ -\x68\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x32\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\ -\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\ -\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\ -\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\x30\ -\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\ -\x3a\x39\x2e\x31\x37\x36\x38\x39\x38\x39\x36\x3b\x73\x74\x72\x6f\ -\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\ -\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\ -\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\ -\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\ -\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x20\ -\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\ -\x75\x72\x6c\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\ -\x65\x6e\x74\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\ -\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\ -\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\ -\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\ -\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\ -\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ -\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x31\ -\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\ -\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x33\x35\ -\x32\x2e\x35\x36\x32\x35\x20\x43\x20\x31\x39\x30\x2e\x39\x33\x37\ -\x35\x32\x2c\x33\x35\x32\x2e\x37\x39\x39\x38\x32\x20\x31\x39\x36\ -\x2e\x38\x33\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\ -\x32\x30\x32\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\x32\x2e\x39\x33\ -\x37\x35\x20\x43\x20\x33\x31\x32\x2e\x38\x30\x30\x31\x34\x2c\x33\ -\x35\x32\x2e\x39\x33\x37\x35\x20\x34\x30\x36\x2e\x39\x35\x30\x34\ -\x39\x2c\x33\x31\x31\x2e\x36\x31\x30\x34\x34\x20\x34\x34\x35\x2e\ -\x30\x36\x32\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\x4c\x20\x34\x34\ -\x35\x2e\x30\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\x35\x20\ -\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\ -\x39\x33\x37\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x34\x22\x20\x2f\x3e\ -\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\x79\x70\x65\x3d\ -\x22\x61\x72\x63\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\ -\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\ -\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\ -\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\ -\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\ -\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\ -\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\ -\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\ -\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\ -\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\ -\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ -\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ -\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x36\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ -\x63\x78\x3d\x22\x33\x30\x33\x2e\x36\x34\x36\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x63\x79\x3d\ -\x22\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x72\x78\x3d\x22\ -\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\ -\x64\x69\x3a\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\x31\x38\x35\x33\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x33\x30\ -\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\ -\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\ -\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\x2c\x31\x32\x39\ -\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\ -\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\ -\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x7a\x22\ -\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\x39\x37\ -\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\x35\ -\x2e\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\ -\x65\x69\x67\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\ -\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x33\x31\x33\x34\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\ -\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x75\x72\x6c\ -\x28\x23\x72\x61\x64\x69\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\ -\x33\x31\x34\x32\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\ -\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\ -\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\ -\x66\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\ -\x74\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\ -\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\ -\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\ -\x68\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\ -\x36\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\ -\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\ -\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\ -\x3a\x31\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\ -\x73\x76\x67\x3e\x0a\ -" - -qt_resource_name = "\ -\x00\x05\ -\x00\x6f\xa6\x53\ -\x00\x69\ -\x00\x63\x00\x6f\x00\x6e\x00\x73\ -\x00\x0f\ -\x01\x19\xe4\x67\ -\x00\x6c\ -\x00\x65\x00\x64\x00\x5f\x00\x72\x00\x65\x00\x64\x00\x5f\x00\x6f\x00\x66\x00\x66\x00\x2e\x00\x73\x00\x76\x00\x67\ -\x00\x0d\ -\x0e\xf5\xe6\x07\ -\x00\x6c\ -\x00\x65\x00\x64\x00\x5f\x00\x67\x00\x72\x00\x65\x00\x65\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ -\x00\x11\ -\x09\x14\xa8\xe7\ -\x00\x6c\ -\x00\x65\x00\x64\x00\x5f\x00\x67\x00\x72\x00\x65\x00\x65\x00\x6e\x00\x5f\x00\x6f\x00\x66\x00\x66\x00\x2e\x00\x73\x00\x76\x00\x67\ -\ -\x00\x0b\ -\x08\x52\x2e\x07\ -\x00\x6c\ -\x00\x65\x00\x64\x00\x5f\x00\x72\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ -" - -qt_resource_struct = "\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\ -\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x39\xd9\ -\x00\x00\x00\x54\x00\x00\x00\x00\x00\x01\x00\x00\x29\x35\ -\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x14\x9b\ -" - -def qInitResources(): - QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) - -def qCleanupResources(): - QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) - -qInitResources()
--- a/plotter/hpgl_qt.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -import numpy -import pylab -import math -import new # monkey patching... - -from PyQt4 import QtGui, QtCore -from PyQt4.QtCore import Qt, SIGNAL - -from hpgl_parser import HPGLParser - -class QHPGLPlotterWidget(HPGLParser, QtGui.QWidget): - pen_styles = {2: [0.4, 0.6], - 3: [0.6, 0.4], - 4: [0.7, 0.1, 0.1, 0.1], - 5: [0.6, 0.1, 0.2, 0.1], - 6: [0.45, 0.1, 0.1, 0.2, 0.1, 0.05], - } - pen_colors = ["black", "green", "red", "blue", "yellow", "cyan", "magenta", - "darkred", "darkblue"] - - def __init__(self, parent=None): - QtGui.QWidget.__init__(self, parent) - l = QtGui.QVBoxLayout(self) - l.setMargin(1) - self.qview = QtGui.QGraphicsView(self) - self.qview.setRenderHints(QtGui.QPainter.Antialiasing) - self.qview.scale(0.5,-0.5) - l = self.layout() - l.addWidget(self.qview) - self.setBackgroundRole(QtGui.QPalette.Base) - self.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Expanding) - self.clear() - HPGLParser.__init__(self) - - def parse(self, data): - HPGLParser.parse(self, data) - self.resize(self.size()) - - def clear(self): - self.qpen = QtGui.QPen(QtCore.Qt.blue) - self.qbrush = QtGui.QBrush(QtCore.Qt.blue) - self.qfont = QtGui.QFont('Courier') - self.qantialiased = False - self.qtransformed = False - self.qscene = QtGui.QGraphicsScene() - self.qview.setScene(self.qscene) - - def _get_PW(self): - return self._pen_width - def _set_PW(self, value): - value = float(value) - #print "set pen width", value, value*300.0/75.0 - #self.qpen.setWidthF(value*300.0/75.0) - self._pen_width = value - pen_width = property(_get_PW, _set_PW) - - def LT(self): - HPGLParser.LT(self) - if self.line_type == 0: - self.qpen.setStyle(Qt.SolidLine) - elif self.line_type == 1: - self.qpen.setStyle(Qt.DotLine) - else: - pattern = numpy.array(self.pen_styles[self.line_type]) - x0, x1, y0, y1 = self.scale - dist = math.sqrt((x1-x0)**2 + (y1-y0)**2) - pattern = self.pattern_len*dist/100.0 * pattern - if self.qpen.widthF()>0: - pattern = pattern/self.qpen.widthF() - self.qpen.setDashPattern(pattern.tolist()) - - def SP(self): - HPGLParser.SP(self) - color = self.pen_colors[self.pen] - if isinstance(color, tuple): - color = QtGui.QColor(*color) - else: - color = QtGui.QColor(color) - self.qpen.setColor(color) - - def parse(self, data): - HPGLParser.parse(self, data) - self.update() - self.qview.fitInView(self.qscene.sceneRect(), Qt.KeepAspectRatio) - - def minimumSizeHint(self): - return QtCore.QSize(100, 100) - - def sizeHint(self): - return QtCore.QSize(400, 400) - - def resizeEvent(self, event): - self.qview.fitInView(self.qscene.sceneRect(), Qt.KeepAspectRatio) - QtGui.QWidget.resizeEvent(self, event) - - def plot_lines(self, points): - if len(points) == 1: - return - pa = QtGui.QPainterPath() - pa.addPolygon(QtGui.QPolygonF([QtCore.QPointF(*p) for p in points])) - self.qscene.addPath(pa, self.qpen) - - def plot_symbols(self, points): - pass - - def plot_string(self, s): - cw, ch = self.get_char_size() - # this is very ugly and so, but I don't understand haw is - # computed string positionning in HPGL... - t0 = self.qscene.addSimpleText(" ", self.qfont) - br = t0.boundingRect() - mw = br.width() - mh = br.height() - self.qscene.removeItem(t0) - # don't ask me why theses constants are here, they seem OK, that's all - t = self.qscene.addSimpleText(s, self.qfont) - t.scale(1.0 * cw/mw, -1.6 * ch/mh) - x, y = self.pos - t.moveBy(x,y) - br = t.boundingRect() - t.moveBy(-0.5*cw+8, math.sqrt(2)*ch - 13) - - -if __name__=='__main__': - import sys - a = QtGui.QApplication(sys.argv) - mw = QtGui.QMainWindow() - w = QtGui.QWidget(mw) - mw.setCentralWidget(w) - - l = QtGui.QVBoxLayout(w) - p = QHPGLPlotterWidget(w) - l.addWidget(p, 1) - b = QtGui.QPushButton("Replot", w) - l.addWidget(b) - def plot(): - p.parse(open(sys.argv[1]).read()) - b.connect(b, SIGNAL('pressed()'), plot) - mw.show() - plot() - a.exec_() -
--- a/plotter/qgpib_plotter.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,347 +0,0 @@ -# - -import os, sys -import time -import glob - -from PyQt4 import QtGui, QtCore, uic -from PyQt4.QtCore import SIGNAL, Qt - -if "-m" in sys.argv: - from gpib_plotter_mockup import GPIBplotter -else: - from gpib_plotter import GPIBplotter -from hpgl_qt import QHPGLPlotterWidget -import hpgl_plotter_rc - -ldir = os.path.abspath(os.path.dirname(__file__)) -sys.path.append(ldir) -form_class, base_class = uic.loadUiType(os.path.join(ldir, "qhpgl_plotter.ui")) - -from qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem -from qpreferences import PointItem, SizeItem, ByteArrayItem -from qpreferences import AbstractPreferences -from qpreferenceseditor import PreferencesEditor - -from tools import str_num_cmp - -class Preferences(AbstractPreferences): - ORGANISATION="Logilab" - APPLICATION="qgpib_plotter" - - _pos = PointItem() - _size = SizeItem() - _appState = ByteArrayItem() - - device = UnicodeItem(default='/dev/ttyUSB0', - name=u'device', - description=u'GPIB device', - group="GPIB settings") - address = IntItem(default=5, min=0, max=16, - name=u'GPIB address', - group="GPIB settings") - - background = ColorItem(default=QtGui.QColor("white"), - name="Background", - group="Colors") - color0 = ColorItem(default=QtGui.QColor("black"), - name="Pen #0", - group="Colors") - color1 = ColorItem(default=QtGui.QColor("green"), - name="Pen #1", - group="Colors") - color2 = ColorItem(default=QtGui.QColor("red"), - name="Pen #2", - group="Colors") - color3 = ColorItem(default=QtGui.QColor("blue"), - name="Pen #3", - group="Colors") - color4 = ColorItem(default=QtGui.QColor("yellow"), - name="Pen #4", - group="Colors") - color5 = ColorItem(default=QtGui.QColor("cyan"), - name="Pen #5", - group="Colors") - color6 = ColorItem(default=QtGui.QColor("magenta"), - name="Pen #6", - group="Colors") - color7 = ColorItem(default=QtGui.QColor("darkred"), - name="Pen #7", - group="Colors") - -class QtHPGLPlotter(QtGui.QMainWindow, form_class): - def __init__(self, parent=None): - QtGui.QMainWindow.__init__(self, parent) - self._plots = {} - self._prefs = Preferences() - self.setupUi() - self.initializeGPIB() - if self._prefs._pos: - self.move(self._prefs._pos) - if self._prefs._size: - self.resize(self._prefs._size) - if self._prefs._appState: - self.restoreState(self._prefs._appState) - self.readPreferences() - - def readPreferences(self): - bg = self._prefs.background - if bg and bg.isValid(): - self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg)) - pen_colors = [self._prefs["color%d"%i] for i in range(8)] - self.plotterWidget.pen_colors = pen_colors - - def replotCurrent(self): - self.currentPlotChanged(self.plotsView.currentIndex()) - - def setupUi(self): - form_class.setupUi(self, self) # call qtdesigner generated form creation - # actions defined in designer - self.connect(self.actionPreferences, SIGNAL('triggered(bool)'), - self.preferencesTriggered) - self.connect(self.actionQuit, SIGNAL('triggered(bool)'), - self.quitTriggered) - self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q')) - self.connect(self.actionOpen, SIGNAL('triggered(bool)'), - self.openTriggered) - self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O')) - self.connect(self.actionSave, SIGNAL('triggered(bool)'), - self.saveTriggered) - self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S')) - self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'), - self.saveAsTriggered) - - self.plotterWidget = QHPGLPlotterWidget(self) - self.setCentralWidget(self.plotterWidget) - - self.connect(self.captureButton, SIGNAL("toggled(bool)"), - self.captureToggled) - - self._plots_list = QtGui.QStringListModel() - self.plotsView.setModel(self._plots_list) - self.connect(self.plotsView, SIGNAL('activated(const QModelIndex&)'), - self.currentPlotChanged) - self.connect(self.plotsView.selectionModel(), - SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)'), - self.currentPlotChanged) - - def currentPlotChanged(self, index, old_index=None): - if index.isValid(): - value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) - - self.plotterWidget.clear() - self.plotterWidget.parse(self._plots[value]) - - def preferencesTriggered(self, checked=False): - PreferencesEditor(self._prefs, self).exec_() - self.readPreferences() - self.replotCurrent() - - def quitTriggered(self, checked=False): - self.close() - - def closeEvent(self, event): - if 1: - #if self.promptForSave(): - self._prefs._pos = self.pos() - self._prefs._size = self.size() - self._prefs._appState = self.saveState() - event.accept() - else: - event.ignore() - - def openTriggered(self, checked=False): - filenames = QtGui.QFileDialog.getOpenFileNames(self, "Open a HPGL file to display", '.', 'HPGL files (*.plt)\nAll files (*)') - self.openFiles(filenames) - self.displayFirst() - - def displayFirst(self): - if not self.plotsView.currentIndex().isValid(): - self.plotsView.setCurrentIndex(self.plotsView.model().index(0, 0)) - - def openFiles(self, filenames): - ok = False - for filename in filenames: - filename = str(filename) - if os.path.exists(filename): - data = open(filename).read() - name = os.path.basename(filename) - name = os.path.splitext(name)[0] - lst = self.plotsView.model().stringList() - lst.append(name) - self._plots[name] = data - self.plotsView.model().setStringList(lst) - ok = True - return ok - - def plotReceived(self, num): - self._receiving = False - self.setReceivingLed() - plot, timestamp = self.captureThread.getPlot(num) - name = "plot_%s"%(num) - lst = self.plotsView.model().stringList() - lst.append(name) - self._plots[name] = plot - self.plotsView.model().setStringList(lst) - - def plotStarted(self): - self._receiving = True - self.setReceivingLed() - - def saveTriggered(self, checked=False): - print "save" - - def saveAsTriggered(self, checked=False): - index = self.plotsView.selectionModel().currentIndex() - if index.isValid(): - filename = QtGui.QFileDialog.getSaveFileName(self, "Selecte a file name to save HPGL file", '.', 'HPGL files (*.plt)\nAll files (*)') - n = index.row() - value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) - open(filename, 'w').write(self._plots[value]) - - - def initializeGPIB(self): - self._online = False - try: - self.gpib_plotter = QGPIBplotter(device=self._prefs.device, - address=self._prefs.address, - ) - self.captureThread = GPIBReceiver(self.gpib_plotter) - self.connect(self.captureThread, SIGNAL('plotReceived(int)'), - self.plotReceived) - self.connect(self.captureThread, SIGNAL('plotStarted()'), - self.plotStarted) - self.captureThread.start() - except Exception, e: - #print e - self.gpib_plotter = None - self.setCaptureLed() - - def captureToggled(self, state): - if state: - if self.gpib_plotter is None: - self.initializeGPIB() - if self.gpib_plotter is None: - QtGui.QMessageBox.critical(self, self.tr("GPIB error"), - self.tr("<b>Unable to initialize GPIB connection</b>.<br>Please check your GPIB dongle and settings.")) - self._online = False - self.setCaptureLed() - return - self._online = True - self.captureThread.startCapture() - else: - if self.captureThread: - self.captureThread.stopCapture() - self._online = False - self.setCaptureLed() - - def setCaptureLed(self): - if self._online: - icn = QtGui.QIcon(':/icons/led_green.svg') - else: - icn = QtGui.QIcon(':/icons/led_green_off.svg') - self.captureButton.setIcon(icn) - self.captureButton.setChecked(self._online) - - def setReceivingLed(self): - if self._receiving: - icn = QtGui.QIcon(':/icons/led_red.svg') - else: - icn = QtGui.QIcon(':/icons/led_red_off.svg') - self.receivingButton.setIcon(icn) - -class QGPIBplotter(GPIBplotter): - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=5): - GPIBplotter.__init__(self, device, baudrate, timeout, address) - self.emitter = None - - def plotStarted(self): - if self.emitter: - self.emitter.emit(SIGNAL('plotStarted()')) - #self.emitter.msleep(1) - -class GPIBReceiver(QtCore.QThread): - def __init__(self, cnx): - QtCore.QThread.__init__(self) - self.gpibplotter = cnx - self.gpibplotter.emitter = self - - self._cancelmutex = QtCore.QMutex() - self._cancel = False - #self._nreceived = 0 - self._plotsmutex = QtCore.QMutex() - self._plots = [] - self._startstopmutex = QtCore.QMutex() - self._startstop = QtCore.QWaitCondition() - self._capturing = False - - def cancel(self): - self._cancelmutex.lock() - self._cancel = True - self._cancelmutex.unlock() - - def startCapture(self): - self._startstop.wakeOne() - - def stopCapture(self): - self._startstopmutex.lock() - self._capturing = False - self._startstopmutex.unlock() - - def run(self): - while 1: - self._cancelmutex.lock() - if self._cancel: - return - self._cancelmutex.unlock() - self._startstopmutex.lock() - if not self._capturing: - self._startstop.wait(self._startstopmutex) - self._capturing = True - self._startstopmutex.unlock() - - plot = self.gpibplotter.load_plot(wait_timeout=0.1) - timestamp = time.time() - if plot: - self._plotsmutex.lock() - self._plots.append((plot, timestamp)) - n = len(self._plots) - self._plotsmutex.unlock() - self.emit(SIGNAL('plotReceived(int)'), n-1) - self.msleep(10) - - def getPlot(self, num): - self._plotsmutex.lock() - try: - return self._plots[num] - finally: - self._plotsmutex.unlock() - -def main(): - import optparse - opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') - opt.add_option('-m', '--mockup', default=False, - action="store_true", - dest='mockup', - help='Use a pseudo GPIB connection (for test purpose)', - ) - opt.add_option('-v', '--verbose', default=False, - action="store_true", - dest="verbose", - help="Verbose mode",) - - options, argv = opt.parse_args(sys.argv) - - a = QtGui.QApplication(argv) - w = QtHPGLPlotter() - files = [f for f in argv[1:] if os.path.isfile(f)] - files.sort(cmp=str_num_cmp) - if w.openFiles(files): - w.displayFirst() - - w.show() - a.exec_() - -if __name__ == '__main__': - main()
--- a/plotter/qhpgl_plotter.ui Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -<ui version="4.0" > - <class>MainWindow</class> - <widget class="QMainWindow" name="MainWindow" > - <property name="geometry" > - <rect> - <x>0</x> - <y>0</y> - <width>507</width> - <height>492</height> - </rect> - </property> - <property name="windowTitle" > - <string>MainWindow</string> - </property> - <widget class="QWidget" name="centralwidget" /> - <widget class="QMenuBar" name="menubar" > - <property name="geometry" > - <rect> - <x>0</x> - <y>0</y> - <width>507</width> - <height>25</height> - </rect> - </property> - <widget class="QMenu" name="menu_File" > - <property name="title" > - <string>&File</string> - </property> - <addaction name="actionOpen" /> - <addaction name="actionSave" /> - <addaction name="actionSaveAs" /> - <addaction name="separator" /> - <addaction name="actionQuit" /> - </widget> - <widget class="QMenu" name="menuEdit" > - <property name="title" > - <string>Edit</string> - </property> - <addaction name="actionPreferences" /> - </widget> - <addaction name="menu_File" /> - <addaction name="menuEdit" /> - </widget> - <widget class="QStatusBar" name="statusbar" /> - <widget class="QDockWidget" name="plotsDock" > - <attribute name="dockWidgetArea" > - <number>1</number> - </attribute> - <widget class="QWidget" name="dockWidgetContents" > - <layout class="QVBoxLayout" > - <property name="spacing" > - <number>4</number> - </property> - <property name="leftMargin" > - <number>1</number> - </property> - <property name="topMargin" > - <number>4</number> - </property> - <property name="rightMargin" > - <number>1</number> - </property> - <property name="bottomMargin" > - <number>1</number> - </property> - <item> - <layout class="QHBoxLayout" > - <item> - <widget class="QToolButton" name="captureButton" > - <property name="minimumSize" > - <size> - <width>0</width> - <height>29</height> - </size> - </property> - <property name="text" > - <string>on line</string> - </property> - <property name="icon" > - <iconset resource="hpgl_plotter.qrc" >:/icons/led_green.svg</iconset> - </property> - <property name="iconSize" > - <size> - <width>16</width> - <height>16</height> - </size> - </property> - <property name="checkable" > - <bool>true</bool> - </property> - <property name="toolButtonStyle" > - <enum>Qt::ToolButtonTextBesideIcon</enum> - </property> - </widget> - </item> - <item> - <spacer> - <property name="orientation" > - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" > - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="receivingButton" > - <property name="text" > - <string/> - </property> - <property name="icon" > - <iconset resource="hpgl_plotter.qrc" >:/icons/led_red_off.svg</iconset> - </property> - <property name="iconSize" > - <size> - <width>16</width> - <height>16</height> - </size> - </property> - <property name="flat" > - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QListView" name="plotsView" /> - </item> - </layout> - </widget> - </widget> - <action name="actionOpen" > - <property name="text" > - <string>&Open</string> - </property> - </action> - <action name="actionSave" > - <property name="text" > - <string>&Save</string> - </property> - </action> - <action name="actionSaveAs" > - <property name="text" > - <string>Save as...</string> - </property> - </action> - <action name="actionQuit" > - <property name="text" > - <string>&Quit</string> - </property> - </action> - <action name="actionPreferences" > - <property name="text" > - <string>Preferences</string> - </property> - </action> - </widget> - <resources> - <include location="hpgl_plotter.qrc" /> - </resources> - <connections/> -</ui>
--- a/prologix.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -""" -prologix -======== - -Module defining a communication object to talk to Prologix USB-GPIB controler. - -""" -import serial -import time - -import pygpib -from pygpib import ConnectionError - -GPIB_CONTROLLER = 1 -GPIB_DEVICE = 0 - -class GPIB(object): - _retries = 15 - def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, - address=0, mode=1): - """ - Create a new GPIB controller for the Prologix USB-GPIB device - located on serial device 'device'. - """ - self._cnx = serial.Serial(port=device, baudrate=baudrate, timeout=timeout) - self._timeout = timeout - - self.set_mode(mode) - self.set_address(address) - - def set_address(self, address): - """ - Set the address of the GPIB device: - - - if the device is the Controller In Charge, this is the - address of the device commands are sent to, - - - if the device is in GPIB_DEVICE mode, this is its address. - """ - self._set_cmd('addr', address) - self._adress = address - - def set_mode(self, mode): - """ - Set GPIB device mode to 'mode': - - - GPIB_CONTROLLER: set the device as the Controller In Charge - on the GPIB bus - - - GPIB_DEVICE: set the device as a standard GPIB device on the - bus. - """ - self._set_cmd('mode', mode) - self._mode = mode - - def set_controller(self): - """ - Set GPIB device the Controller In Charge on the GPIB bus. - """ - self.set_mode(1) - - def set_device(self): - """ - Set the GPIB device as a simple device on the GPIB bus. - """ - self.set_mode(0) - - def send_command(self, cmd): - """ - Send the specified GPIB command on the bus (must be the CIC), - and read the answer. - """ - assert self._mode == 1 - self._cnx.write(cmd+'\r') - time.sleep(self._timeout) # required? - ret = self._cnx.readlines() - return ''.join(ret) - - def check_srq(self): - """ - Check the SRQ line - """ - assert self._mode == 1, "must be the Controller In Charge" - self._cnx.write('++srq\r') - ret = self._cnx.readline().strip() - if ret: - return bool(int(ret)) - return None - - def poll(self): - """ - Poll every address, and return a dictionnary - {add: status, ...} - """ - assert self._mode == 1, "must be the Controller In Charge" - dico = {} - for add in range(31): - self._cnx.write('++spoll %d\r'%i) - ret = self._cnx.readline().strip() - if ret: - dico[i] = int(ret) - return dico - - def _read(self): - for i in range(self._retries): - rdata = self._cnx.readline() - if rdata.strip() != "": - break - time.sleep(self._timeout) - return rdata - - def _set_cmd(self, cmd, value): - self._cnx.write('++%s %d\r'%(cmd, value)) - self._cnx.write('++%s\r'%(cmd)) - rval = self._read().strip() - if not rval.isdigit() or int(rval) != value: - raise ConnectionError("Can't set GPIB %s to %s [ret=%s]"%(cmd, value, repr(rval)))
--- a/pygpib.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -""" -gpib: create serial connection to GPIB-USB device (ProLogix is the -only supported device for now). -""" -import serial -from serial.serialutil import SerialException -import time - -class ConnectionError(Exception): - pass - -class Constants(object): - def __init__(self): - self.constants = {} - self.descriptions = {} - self.rev_constants = {} - for v, k, m in self._constants: - self.k = v - self.constants[v] = k - self.rev_constants[k] = v - self.descriptions[v] = m - - def __getitem__(self, k): - if isinstance(k, basestring): - return self.rev_constants[k] - else: - return self.constants[k] - - def get_description(self, k): - if isinstance(k, basestring): - k = self.rev_constants[k] - return self.descriptions[k] - - -class MODE(Constants): - _constants = [(1, "CONTROLLER", "Set device as Controller in Charge"), - (0, "DEVICE", "Set device as simple listener"), - ] -# TODO -# class STATUS_BYTE(Constants): -# # IEEE 488.2 Status Byte constants -# MAV = 0x10 # Message AVailable: bit 4 of the Status Byte -# ESB = 0x20 # Event Status Bit: bit 5 of the Status Byte -# MSS = 0x40 # Master Summary Status bit: bit 6 of the Status Byte (NOT -# # sent in response to a serial poll) -# RQS = 0x40 # Request Service: bit 6 of the Status Byte (when sent in -# # response to a serial poll) -# class SESR(Constants): -# # SESR constants (Standard Event Status Register) -# PON = 0x80 # Power On: Power has been turned On since last register -# # read access -# URQ = 0x40 # User Request: the user has activated some device control -# # (whatever the Remote Local state is) -# CME = 0x20 # Command Error -# EXE = 0x10 # Execution Error -# DDE = 0x08 # Device Dependant Error -# QYE = 0x04 # QuerY Error (attempt to read data while Output Queue is -# # empty, or data in the OQ was lost) -# RQC = 0x02 # Request Control: tell the CiC that the device wants to -# # become the CiC -# OPC = 0x01 # Operation Complete: device has completed any pending -# # operation (ready to accept new commands). This bit is -# # generated in response to a OPC command. -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/__init__.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,242 @@ +""" +HP3562A +======= + +Module for communicating with the HP 3562A Digital Signal Analyzer. + +Subpackages +----------- + + +Constants +--------- + +""" +import struct +import re +import pygpibtoolkit.pygpib as gpib +import numpy + +######################################## +# HP3562A internal binary types decoders + +def decode_float(s): + if len(s) == 4: + i1, i2, e = struct.unpack('>hbb', s) + i2 = i2 * 2**(-23) + return (i1/32768. + i2)*2**e + else: + i1, i2, i3, i4, e = struct.unpack('>hhhbb', s) + if i2 < 0: + i2 = (i2+32768.)*2**(-15) + else: + i2 = i2*2**(-15) + if i3 < 0: + i3 = (i3+32768.)*2**(-30) + else: + i3 = i3*2**(-15) + i4 = i4 * 2**(-38) + return (i1+i2+i3+i4)*2**(e-15) + +# def decode_float(s): +# assert len(s) in [4,8] +# # exponential term +# e = ord(s[-1]) +# if e & 0x80: +# e = e - 256 + +# # mantissa +# m = [ord(x) for x in s[:-1]] +# M = 0. +# for i in range(len(s)-1): +# #M += m[i]<<(i*8) +# M += float(m[i])/2**((i+1)*8) +# # XXX how do we deal negative numbers? +# #if m[0] & 0x80: +# # M = M - 2^(len(s)) +# return M * 2**(e+1) + +def decode_string(s): + nb = ord(s[0]) + s = s[1:nb+2] + r = "" + # XXX why do we need to do this? It's not described in the manual... + for c in s: + r += chr(ord(c) & 0x7F) + return r + + +### +# Some useful functions +def format_header(header, head_struct, columns=80): + """ + Pretty print a data block (trace, state or coord) + """ + bool_re = re.compile(r'((?P<before>.*) )?(?P<flag>\w+/\w+)( (?P<after>.*))?') + + todisp = [] + for row in head_struct: + key = row[0] + typ = row[1] + if typ is None: + continue + val = header.get(key, "N/A") + if isinstance(val, basestring): + val = repr(val) + elif typ is bool and isinstance(val, typ): + m = bool_re.match(key) + if m: + d = m.groupdict() + key = "" + if d['before']: + key += d['before'] + if d['after']: + key += d['after'] + key = key.capitalize() + val = d['flag'].split('/')[not val] + else: + val = str(val) + else: + val = str(val) + todisp.append((key+":", val)) + maxk = max([len(k) for k, v in todisp]) + maxv = max([len(v) for k, v in todisp]) + fmt = "%%-%ds %%-%ds"%(maxk, maxv) + w = maxk+maxv+4 + ncols = columns/w + if ncols: + nrows = len(todisp)/ncols + else: + nrows = len(todisp) + res = "" + for i in range(nrows): + res += "| ".join([fmt%todisp[j*nrows+i] for j in range(ncols)]) + "\n" + return res + +def decode_header(data, header_struct, idx=0): + d = data + if d[idx:].startswith('#'): + # we have a preliminary header here... + typ = d[idx:idx+2] + assert typ == "#A" + idx += 2 + totlen = struct.unpack('>h', d[idx:idx+2])[0] + idx += 2 + tt=0 + header = {} + for i, (nam, dtype, fmt, nbytes) in enumerate(header_struct): + if dtype is None: + idx += nbytes + continue + elif dtype == str: + val = decode_string(d[idx:]) + else: + if fmt: + v = struct.unpack('>'+fmt, d[idx: idx+nbytes])[0] + if isinstance(dtype, dict): + val = dtype.get(int(v), "N/A") + else: + val = dtype(v) + else: + val = dtype(d[idx: idx+nbytes]) + header[nam] = val + idx += nbytes + return header, idx + +def read_trace(data, idx, nelts): + assert len(data[idx:]) >= (nelts*4), "data[idx:] is too small (%s for %s)"%(len(data[idx:]), (nelts*4)) + resu = [] + for i in range(nelts): + resu.append(decode_float(data[idx: idx+4])) + idx += 4 + return numpy.array(resu, dtype=float) + +##################### +# HP3562A constants + +# GPIB buffer size is 3x80 characters lines +class STATUS_BYTE(gpib.Constants): + # HP3562A Status Byte, as returned by a serial poll + _constants = [(0x40, "RQS", "Request Service"), # when sent in response to a serial poll + (0x20, "ERR", "GPIB error"), + (0x10, "RDY", "ready to accept GPIB commands"), + ] + + conditions = [(0, "NSR", "No service requested"), + (1, "USRQ1", "User SRQ #1"), + (2, "USRQ1", "User SRQ #2"), + (3, "USRQ1", "User SRQ #3"), + (4, "USRQ1", "User SRQ #4"), + (5, "USRQ1", "User SRQ #5"), + (6, "USRQ1", "User SRQ #6"), + (7, "USRQ1", "User SRQ #7"), + (8, "USRQ1", "User SRQ #8"), + (9, "EOD", "End of disk action"), + (10, "EOP", "End of plot action"), + (11, "STCH", "Instrument status changed"), # any change in + # the status register sets this condition + (12, "PWR", "Power on"), + (13, "KEY", "Key pressed"), + (14, "DCP", "Device clear plotter (listen)"), + # ... + ] + def __init__(self): + super(STATUS_BYTE, self).__init__() + self._conditions = dict([(x[0], x[1]) for x in self.conditions]) + self._rev_conditions = dict([(x[1], x[0]) for x in self.conditions]) + self._long_conditions = dict([(x[0], x[2]) for x in self.conditions]) + + def byte_condition(self, byte): + byte = byte & 0x8F + return self._conditions.get(byte, "N/A") + +class IS_REGISTER(gpib.Constants): + _constants = [(0x01, "MEASP", "measeurement pause"), + (0x02, "ASQP", "Auto sequence pause"), + (0X04, "EOM", "End of measurement, capture or throughput"), + (0x08, "EOAS", "End of auto sequence"), + (0x10, "SWPR", "Sweep point ready"), + (0x20, "CH1OV", "Channel 1 overrange"), + (0x40, "CH2OV", "Channel 2 overrange"), + (0X80, "CH1HR", "Channel 1 half range"), + (0x100, "CH2HR", "Channel 2 half range"), + (0x200, "SFALT", "Source falt"), + (0x400, "RUNL", "Reference unlock"), + (0x800, "RMKT", "Remote marker knob turn"), + (0x1000, "REKT", "Remote entry knob turn"), + (0x2000, "ASRC", "Asctive Status Register changed"), + (0x4000, "PWRF", "Power-on test failed"), + ] + +class StatusQuery(gpib.Constants): + _command = "STA?" + _constants = [(0x01, "N/A", "Not used"), + (0x02, "N/A", "Not used"), + (0x04, "KEY", "Key pressed"), + (0x08, "N/A", "Not used"), + (0x10, "RDY", "Ready"), + (0x20, "ERR", "Error"), + (0x40, "RQS", "Request"), + (0x80, "MOS", "Message on screen"), + (0x100, "MEASP", "measeurement pause"), + (0x200, "ASQP", "Auto sequence pause"), + (0X400, "EOM", "End of measurement, capture or throughput"), + (0x800, "EOAS", "End of auto sequence"), + (0x1000, "SWPR", "Sweep point ready"), + (0x2000, "CH1OV", "Channel 1 overrange"), + (0x4000, "CH2OV", "Channel 2 overrange"), + (0x8000, "MAOV", "Math overflow"), + ] +class ActivityStatysRegister(gpib.Constants): + _command = "AS?" + _constants = [(0x01, "CKFL", "Check fault log"), + (0x02, "FITR", "Filling time record"), + (0x04, "FLTR", "Filters settings"), + (0x08, "CFTP", "Curve fir in progress"), + (0x10, "MSSM", "Missed sample"), + (0x20, "TMPR", "Timed preview"), + (0x40, "ACDA", "Accept date"), + #... + ] + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/coord_decoder.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +""" +state_decoder +============= + +Module for decoding the internal state of the HP3562A DSA, using the +GPIB command "DSBN" (Dump State BiNary). + +This file can be exectued as a python script. Use '-h' for more informations. +""" + +from pygpibtoolkit.HP3562A import format_header, decode_float, decode_string, decode_header, read_trace +from pygpibtoolkit.HP3562A.trace_decoder import decode_trace, HEADER as TRACE_HEADER +from pygpibtoolkit.HP3562A.enum_types import * + +HEADER = [("Y coordinates", EYCOORD, 'h', 2), + ("# of disp elements", int, "h", 2), + ("First element", int, "h", 2), + ("Total elements", int, 'h', 2), + ("Display sampling", EDISPSMP, 'h', 2), + ("Scaling", ESCAL, 'h', 2), + ("Data pointer", long, 'l', 4), + ("In data", long, 'l', 4), + ("Log/linear x-axis", bool, 'h', 2), + ("Sampled display data", bool, 'h', 2), + ("Plot/Graph mode", bool, 'h', 2), + ("Phase wrap", bool, 'h', 2), + ("Not used", None, None, 36), + ("X scale factor", decode_float, None, 4), + ("Grid min Y scale", decode_float, None, 4), + ("Grid max Y scale", decode_float, None, 4), + ("/div", decode_float, None, 4), + ("Min value of data", decode_float, None, 4), + ("Max value of data", decode_float, None, 4), + ("Y cumulative min", decode_float, None, 4), + ("Y cumulative max", decode_float, None, 4), + ("Y scale factor", decode_float, None, 4), + ("Not used", None, None, 16), + ("Stop value", decode_float, None, 8), + ("Left grid", decode_float, None, 8), + ("Right grid", decode_float, None, 8), + ("Left data", decode_float, None, 8), + ("Right data", decode_float, None, 8), + ] + +def decode_coord(data): + """ + Decode the data (as generated by the HP3562A DSA in response to a + "DSBN" command), and returns a dict (header) + + header is the dictionnary of the header of the dumped data block, + """ + header, idx = decode_header(data, HEADER) + trace_header, idx = decode_header(data, TRACE_HEADER, idx) + trace = read_trace(data, idx, trace_header["Number of elements"]) + return header, trace_header, trace + + +def main(): + import sys + import optparse + import numpy + + opt = optparse.OptionParser("A simple tool for displaying dumped coord data block") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, read from stdin') + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + opt.add_option('-d', '--display-header', default=False, + action="store_true", + dest="displayheader", + help="Display the trace header") + opt.add_option('-P', '--noplot-trace', default=True, + action="store_false", + dest="plot", + help="Do not display the plot of the trace") + opt.add_option('-x', '--xmode', default='lin', + dest='xmode', + help='X coordinate mode (may be "lin" [default] or "log")') + opt.add_option('-y', '--ymode', default='lin', + dest='ymode', + help='Y coordinate mode (may be "lin" [default], "log" or "db")') + + options, argv = opt.parse_args(sys.argv) + + + if options.filename is None: + print "Can't deal stdin for now..." + sys.exit(1) + try: + coord_header, header, data = decode_coord(open(options.filename, 'rb').read()) + except Exception, e: + print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename + print e + sys.exit(1) + + if options.displayheader: + print format_header(coord_header, HEADER, 100) + print format_header(header, TRACE_HEADER, 100) + if options.plot: + f0 = header['Start freq value'] + dx = header['Delta X-axis'] + n = header['Number of elements'] + x = numpy.linspace(f0, f0+dx*n, len(data)) + y = data.copy() + + ym = coord_header['Min value of data'] + yM = coord_header['Max value of data'] + ys = coord_header['Y scale factor'] + + y[y<ym] = ym + y *= ys + + import pylab + pylab.ylabel(header['Amplitude units']) + pylab.grid() + pylab.plot(x, y) + pylab.xlabel('frequency (%s)'%header["X axis units"]) + pylab.ylabel("%s (%s)"%(coord_header['Y coordinates'], header['Amplitude units']) ) + pylab.show() + +if __name__ == "__main__": + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/dump_datablock.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,87 @@ +import sys +import time +import gpib +import prologix + + +class HP3562dumper(prologix.GPIB): + """ + Class dedicated to dump data blocks from HP3562A DSA (trace, + internal state or coordinate). + """ + MODES = {'trace': 'DD', + 'state': 'DS', + 'coord': 'DC', + } + + FORMATS = {'binary': 'BN', + 'ascii': 'AS', + 'ansi': 'AN'} + + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=0): + super(HP3562dumper, self).__init__(device, baudrate, timeout, address, mode=1) + + def dump(self, mode='trace', format="binary"): + """ + Dump the required data block and return it as a raw string buffer. + + 'mode' can be 'trace' [default], 'state' or 'coord'. + 'format' can be 'binary' [default], 'ascii' or 'ansi'. + """ + format = format.lower() + mode = mode.lower() + assert mode in self.MODES + assert format in self.FORMATS + cmd = self.MODES[mode] + self.FORMATS[format] + + res = "" + print "command = ", cmd + self._cnx.write('%s\r'%cmd) + i = 0 + while i<self._retries: + l = self._cnx.readline() + if l.strip() == "": + i += 1 + time.sleep(self._timeout) + continue + res += l + i = 0 + return res + + + +def main(): + import optparse + opt = optparse.OptionParser("A simple tool for dumping the current trace") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, write to stdout') + opt.add_option('-d', '--device', default='/dev/ttyUSB0', + dest='device', + help='Device of the RS232 connection (default: /dev/ttyUSB0)', + ) + opt.add_option('-a', '--address', default=0, + dest='address', + help='GPIB address of the device', + ) + opt.add_option('-b', '--block', default='trace', + dest='block', + help='Data block to dump (may be "trace" [default], "state" or "coord")', + ) + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + options, argv = opt.parse_args(sys.argv) + + cnx = HP3562dumper(device=options.device, address=int(options.address)) + res = cnx.dump(mode=options.block, format=options.mode) + sys.stderr.write("read %s bytes\n"%(len(res))) + if options.filename: + open(options.filename, 'w').write(res) + else: + print res + +if __name__=='__main__': + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/enum_types.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- +""" +Constants used for HP3562A data block interpretation. +""" + +EDSP = {0: "No data", + 1: "Frequency response", + 2: "Power spectrum 1", + 3: "Power spectrum 2", + 4: "Coherence", + 5: "Cross spectrum", + 6: "Input time 1", + 7: "Input time 2", + 8: "Input linear spectrum 1", + 9: "Input linear spectrum 2", + 10: "Impulse response", + 11: "Cross correlation", + 12: "Auto correlation 1", + 13: "Auto correlation 2", + 14: "Histogram 1", + 15: "Histogram 2", + 16: "Cumulative density function 1", + 17: "Cumulative density function 2", + 18: "Probability density function 1", + 19: "Probability density function 2", + 20: "Average linear spectrum 1", + 21: "Average linear spectrum 2", + 22: "Average time record 1", + 23: "Average time record 2", + 24: "Synthesis pole-zeros", + 25: "Synthesis pole-residue", + 26: "Synthesis polynomial", + 27: "Synthesis constant", + 28: "Windowed time record 1", + 29: "Windowed time record 2", + 30: "Windowed linear spectrum 1", + 31: "Windowed linear spectrum 2", + 32: "Filtered time record 1", + 33: "Filtered time record 2", + 34: "Filtered linear spectrum 1", + 35: "Filtered linear spectrum 2", + 36: "Time capture buffer", + 37: "Captured linear spectrum", + 38: "Captured time record", + 39: "Throughput time record 1", + 40: "Throughput time record 2", + 41: "Curve fit", + 42: "Weighted function", + 43: "Not used", + 44: "Orbits", + 45: "Demodulation polar", + 46: "Preview demod record 1", + 47: "Preview demod record 2", + 48: "Preview demod linear spectrum 1", + 49: "Preview demod linear spectrum 2", + } + +ECH = {0: "Channel 1", + 1: "Channel 2", + 2: "Channel 1&2", + 3: "No channel", + } + +EOVR = ECH + +EDOM = {0: 'Time', + 1: 'Frequency', + 2: 'Voltage (amplitude)', + } + +EVLT = {0: "Peak", + 1: "RMS", + 2: "Volt (indicates peak only)", + } + +EAMP = {0: u"Volts", + 1: u"Volts²", + 2: u"PSD (V²/Hz)", + 3: u"ESD (V²s/Hz)", + 4: u"PSD¹² (V/Hz¹²)", + 5: u"No unit", + 6: u"Unit volts", + 7: u"Unit volts²", + } + +EXAXIS= {0: u"No units", + 1: u"Hertz", + 2: u"RPM", + 3: u"Orders", + 4: u"Seconds", + 5: u"Revs", + 6: u"Degrees", + 7: u"dB", + 8: u"dBV", + 9: u"Volts", + 10: u"V\u221AHz", + 11: u"Hz/s", + 12: u"V/EU", + 13: u"Vrms", + 14: u"V²/Hz", + 15: u"%", + 16: u"Points", + 17: u"Records", + 18: u"Ohms", + 19: u"Hertz/octave", + 20: u"Pulse/Rev", + 21: u"Decades", + 22: u"Minutes", + 23: u"V²s/Hz", + 24: u"Octave", + 25: u"Seconds/Decade", + 26: u"Seconds/Octave", + 27: u"Hz/Point", + 28: u"Points/Sweep", + 29: u"Points/Decade", + 30: u"Points/Octave", + 31: u"V/Vrms", + 32: u"V²", + 33: u"EU referenced to chan 1", + 34: u"EU referenced to chan 2", + 35: u"EU value", + } + +EMEAS = {0: "Linear resolution", + 1: "Log resolution", + 2: "Swept sine", + 3: "Time capture", + 4: "Linear resolution throughput", + } + +EDEMOD = {45: "AM", + 46: "FM", + 47: "PM", + } + +EAVG = {0: "No data", + 1: "Not averaged", + 2: "Averaged",} + +EWIN = {0: "N/A", + 1: "Hann", + 2: "Flat top", + 3: "Uniforme", + 4: "Exponential", + 5: "Force", + 6: "Force chan 1/expon chan 2", + 7: "Expon chan 1/force chan 2", + 8: "User", + } + +EMTYP = {0: "Frequency responce", + 1: "Cross correlation", + 2: "Power spectrum", + 3: "Auto correlation", + 4: "Histogram", + 5: "No measurement", + } + +EWINTYP = { 11: "Hanning", + 12: "Flat top", + 13: "Uniform", + 14: "User window", + 15: "Force/Exponential", + } + +EFEXPW = { 0: "Force", + 1: "Exponential", + } + +EAVGTYP = { 6: "Stable", + 7: "Exponential", + 8: "Peak", + 9: "Continuous peak", + 10: "Averaging off", + } + +ETRGTYP = { 18: "Free run", + 19: "Channel 1", + 20: "Channel 2", + 21: "External", + 22: "Source trigger", + 23: "HP-IB trigger", + } + +ETRGSLP = { 16: "Positive", + 17: "Negative", + } + +EPRVTYP = { 0: "Manual preview", + 1: "Timed preview", + 2: "Preview off", + } + +ESMPTYP = { 24: "Internal sample", + 25: "External sample", + } + +ERNGUNT = EXAXIS + +ERNGTYP = { 26: "Auto range on", + 27: "Auto range off", + 28: "Auto range set", + } + +EINCPL = { 29: "AC", + 30: "DC", + } + +ESRCTYP = { 31: "Source off", + 32: "Random noise", + 33: "Burst random", + 34: "Periodic chirp", + 35: "Burst chirp", + 36: "Swept sine", + 37: "Fixed sine", + } + +ESWPDIR = { 41: "Up", + 42: "Sweep hold", + 43: "Manual sweep", + 44: "Down", + } + +ESWPMOD = { 39: "Linear sweep", + 40: "Log sweep", + } + +EEXTSMPFREQUNT = EXAXIS + +EBNDUNT = EXAXIS + +ESWPRATEUNT = EXAXIS + +EAUGAINREFCH = { 0: "Channel 1", + 1: "Channel 2", + 2: "Not used", + 3: "No channel", + } +EDEMODCH = { 0: "Channel 1", + 1: "Channel 2", + 2: "Both channels", + 3: "No channel", + } + +ESRCLVLUNT = EXAXIS + +ESRCDCOFFST = EXAXIS + +ETRGLVLUNT = EXAXIS + +ECPTLGHUNT = EXAXIS + +EYCOORD = { 1: "Real", + 2: "Imaginary", + 3: "Linear magnitude", + 4: "Log magnitude", + 5: "dB", + 6: "Nyquist", + 7: "Not used", + 8: "Phase", + 9: "Nichols", + 10: "dBm", + } + +EDISPSMP = { 0: "Not sampled", # #displayed elements = total elements + 1: "Half sampled", # #displayed elements = total elements/2 + 2: "Sampled", # #displayed elements < total elements + } +ESCAL = { 0: "X and Y auto scale", + 1: "X fixed, Y auto scale", + 2: "X auto scale, Y fixed", + 3: "X and Y fixed", + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/q3562A.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,346 @@ +# + +import os, sys +import time +import glob + +from PyQt4 import QtGui, QtCore, uic +from PyQt4.QtCore import SIGNAL, Qt + +if "-m" in sys.argv: + from dump_datablock_mockup import HP3562dumper +else: + from dump_datablock import HP3562dumper +import q3562A_rc + +ldir = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(ldir) +form_class, base_class = uic.loadUiType(os.path.join(ldir, "q3562A.ui")) + +from qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem +from qpreferences import PointItem, SizeItem, ByteArrayItem +from qpreferences import AbstractPreferences +from qpreferenceseditor import PreferencesEditor + +from tools import str_num_cmp + +class Preferences(AbstractPreferences): + ORGANISATION="Logilab" + APPLICATION="qgpib_plotter" + + _pos = PointItem() + _size = SizeItem() + _appState = ByteArrayItem() + + device = UnicodeItem(default='/dev/ttyUSB0', + name=u'device', + description=u'GPIB device', + group="GPIB settings") + address = IntItem(default=5, min=0, max=16, + name=u'GPIB address', + group="GPIB settings") + + background = ColorItem(default=QtGui.QColor("white"), + name="Background", + group="Colors") + color0 = ColorItem(default=QtGui.QColor("black"), + name="Pen #0", + group="Colors") + color1 = ColorItem(default=QtGui.QColor("green"), + name="Pen #1", + group="Colors") + color2 = ColorItem(default=QtGui.QColor("red"), + name="Pen #2", + group="Colors") + color3 = ColorItem(default=QtGui.QColor("blue"), + name="Pen #3", + group="Colors") + color4 = ColorItem(default=QtGui.QColor("yellow"), + name="Pen #4", + group="Colors") + color5 = ColorItem(default=QtGui.QColor("cyan"), + name="Pen #5", + group="Colors") + color6 = ColorItem(default=QtGui.QColor("magenta"), + name="Pen #6", + group="Colors") + color7 = ColorItem(default=QtGui.QColor("darkred"), + name="Pen #7", + group="Colors") + +class QtHPGLPlotter(QtGui.QMainWindow, form_class): + def __init__(self, parent=None): + QtGui.QMainWindow.__init__(self, parent) + self._plots = {} + self._prefs = Preferences() + self.setupUi() + self.initializeGPIB() + if self._prefs._pos: + self.move(self._prefs._pos) + if self._prefs._size: + self.resize(self._prefs._size) + if self._prefs._appState: + self.restoreState(self._prefs._appState) + self.readPreferences() + + def readPreferences(self): + bg = self._prefs.background + if bg and bg.isValid(): + self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg)) + pen_colors = [self._prefs["color%d"%i] for i in range(8)] + self.plotterWidget.pen_colors = pen_colors + + def replotCurrent(self): + self.currentPlotChanged(self.plotsView.currentIndex()) + + def setupUi(self): + form_class.setupUi(self, self) # call qtdesigner generated form creation + # actions defined in designer + self.connect(self.actionPreferences, SIGNAL('triggered(bool)'), + self.preferencesTriggered) + self.connect(self.actionQuit, SIGNAL('triggered(bool)'), + self.quitTriggered) + self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q')) + self.connect(self.actionOpen, SIGNAL('triggered(bool)'), + self.openTriggered) + self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O')) + self.connect(self.actionSave, SIGNAL('triggered(bool)'), + self.saveTriggered) + self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S')) + self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'), + self.saveAsTriggered) + + self.plotterWidget = QHPGLPlotterWidget(self) + self.setCentralWidget(self.plotterWidget) + + self.connect(self.captureButton, SIGNAL("toggled(bool)"), + self.captureToggled) + + self._plots_list = QtGui.QStringListModel() + self.plotsView.setModel(self._plots_list) + self.connect(self.plotsView, SIGNAL('activated(const QModelIndex&)'), + self.currentPlotChanged) + self.connect(self.plotsView.selectionModel(), + SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)'), + self.currentPlotChanged) + + def currentPlotChanged(self, index, old_index=None): + if index.isValid(): + value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) + + self.plotterWidget.clear() + self.plotterWidget.parse(self._plots[value]) + + def preferencesTriggered(self, checked=False): + PreferencesEditor(self._prefs, self).exec_() + self.readPreferences() + self.replotCurrent() + + def quitTriggered(self, checked=False): + self.close() + + def closeEvent(self, event): + if 1: + #if self.promptForSave(): + self._prefs._pos = self.pos() + self._prefs._size = self.size() + self._prefs._appState = self.saveState() + event.accept() + else: + event.ignore() + + def openTriggered(self, checked=False): + filenames = QtGui.QFileDialog.getOpenFileNames(self, "Open a HPGL file to display", '.', 'HPGL files (*.plt)\nAll files (*)') + self.openFiles(filenames) + self.displayFirst() + + def displayFirst(self): + if not self.plotsView.currentIndex().isValid(): + self.plotsView.setCurrentIndex(self.plotsView.model().index(0, 0)) + + def openFiles(self, filenames): + ok = False + for filename in filenames: + filename = str(filename) + if os.path.exists(filename): + data = open(filename).read() + name = os.path.basename(filename) + name = os.path.splitext(name)[0] + lst = self.plotsView.model().stringList() + lst.append(name) + self._plots[name] = data + self.plotsView.model().setStringList(lst) + ok = True + return ok + + def plotReceived(self, num): + self._receiving = False + self.setReceivingLed() + plot, timestamp = self.captureThread.getPlot(num) + name = "plot_%s"%(num) + lst = self.plotsView.model().stringList() + lst.append(name) + self._plots[name] = plot + self.plotsView.model().setStringList(lst) + + def plotStarted(self): + self._receiving = True + self.setReceivingLed() + + def saveTriggered(self, checked=False): + print "save" + + def saveAsTriggered(self, checked=False): + index = self.plotsView.selectionModel().currentIndex() + if index.isValid(): + filename = QtGui.QFileDialog.getSaveFileName(self, "Selecte a file name to save HPGL file", '.', 'HPGL files (*.plt)\nAll files (*)') + n = index.row() + value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) + open(filename, 'w').write(self._plots[value]) + + + def initializeGPIB(self): + self._online = False + try: + self.gpib_plotter = QGPIBplotter(device=self._prefs.device, + address=self._prefs.address, + ) + self.captureThread = GPIBReceiver(self.gpib_plotter) + self.connect(self.captureThread, SIGNAL('plotReceived(int)'), + self.plotReceived) + self.connect(self.captureThread, SIGNAL('plotStarted()'), + self.plotStarted) + self.captureThread.start() + except Exception, e: + #print e + self.gpib_plotter = None + self.setCaptureLed() + + def captureToggled(self, state): + if state: + if self.gpib_plotter is None: + self.initializeGPIB() + if self.gpib_plotter is None: + QtGui.QMessageBox.critical(self, self.tr("GPIB error"), + self.tr("<b>Unable to initialize GPIB connection</b>.<br>Please check your GPIB dongle and settings.")) + self._online = False + self.setCaptureLed() + return + self._online = True + self.captureThread.startCapture() + else: + if self.captureThread: + self.captureThread.stopCapture() + self._online = False + self.setCaptureLed() + + def setCaptureLed(self): + if self._online: + icn = QtGui.QIcon(':/icons/led_green.svg') + else: + icn = QtGui.QIcon(':/icons/led_green_off.svg') + self.captureButton.setIcon(icn) + self.captureButton.setChecked(self._online) + + def setReceivingLed(self): + if self._receiving: + icn = QtGui.QIcon(':/icons/led_red.svg') + else: + icn = QtGui.QIcon(':/icons/led_red_off.svg') + self.receivingButton.setIcon(icn) + +class QGPIBplotter(GPIBplotter): + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=5): + GPIBplotter.__init__(self, device, baudrate, timeout, address) + self.emitter = None + + def plotStarted(self): + if self.emitter: + self.emitter.emit(SIGNAL('plotStarted()')) + #self.emitter.msleep(1) + +class GPIBReceiver(QtCore.QThread): + def __init__(self, cnx): + QtCore.QThread.__init__(self) + self.gpibplotter = cnx + self.gpibplotter.emitter = self + + self._cancelmutex = QtCore.QMutex() + self._cancel = False + #self._nreceived = 0 + self._plotsmutex = QtCore.QMutex() + self._plots = [] + self._startstopmutex = QtCore.QMutex() + self._startstop = QtCore.QWaitCondition() + self._capturing = False + + def cancel(self): + self._cancelmutex.lock() + self._cancel = True + self._cancelmutex.unlock() + + def startCapture(self): + self._startstop.wakeOne() + + def stopCapture(self): + self._startstopmutex.lock() + self._capturing = False + self._startstopmutex.unlock() + + def run(self): + while 1: + self._cancelmutex.lock() + if self._cancel: + return + self._cancelmutex.unlock() + self._startstopmutex.lock() + if not self._capturing: + self._startstop.wait(self._startstopmutex) + self._capturing = True + self._startstopmutex.unlock() + + plot = self.gpibplotter.load_plot(wait_timeout=0.1) + timestamp = time.time() + if plot: + self._plotsmutex.lock() + self._plots.append((plot, timestamp)) + n = len(self._plots) + self._plotsmutex.unlock() + self.emit(SIGNAL('plotReceived(int)'), n-1) + self.msleep(10) + + def getPlot(self, num): + self._plotsmutex.lock() + try: + return self._plots[num] + finally: + self._plotsmutex.unlock() + +def main(): + import optparse + opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') + opt.add_option('-m', '--mockup', default=False, + action="store_true", + dest='mockup', + help='Use a pseudo GPIB connection (for test purpose)', + ) + opt.add_option('-v', '--verbose', default=False, + action="store_true", + dest="verbose", + help="Verbose mode",) + + options, argv = opt.parse_args(sys.argv) + + a = QtGui.QApplication(argv) + w = QtHPGLPlotter() + files = [f for f in argv[1:] if os.path.isfile(f)] + files.sort(cmp=str_num_cmp) + if w.openFiles(files): + w.displayFirst() + + w.show() + a.exec_() + +if __name__ == '__main__': + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/q3562A.ui Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,179 @@ +<ui version="4.0" > + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>507</width> + <height>492</height> + </rect> + </property> + <property name="windowTitle" > + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget" /> + <widget class="QMenuBar" name="menubar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>507</width> + <height>25</height> + </rect> + </property> + <widget class="QMenu" name="menu_File" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="actionOpen" /> + <addaction name="actionSave" /> + <addaction name="actionSaveAs" /> + <addaction name="separator" /> + <addaction name="actionQuit" /> + </widget> + <widget class="QMenu" name="menuEdit" > + <property name="title" > + <string>Edit</string> + </property> + <addaction name="actionPreferences" /> + </widget> + <addaction name="menu_File" /> + <addaction name="menuEdit" /> + </widget> + <widget class="QStatusBar" name="statusbar" /> + <widget class="QDockWidget" name="plotsDock" > + <attribute name="dockWidgetArea" > + <number>1</number> + </attribute> + <widget class="QWidget" name="dockWidgetContents" > + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>4</number> + </property> + <property name="leftMargin" > + <number>1</number> + </property> + <property name="topMargin" > + <number>4</number> + </property> + <property name="rightMargin" > + <number>1</number> + </property> + <property name="bottomMargin" > + <number>1</number> + </property> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QToolButton" name="getTraceButton" > + <property name="minimumSize" > + <size> + <width>0</width> + <height>29</height> + </size> + </property> + <property name="text" > + <string>get trace</string> + </property> + <property name="icon" > + <iconset/> + </property> + <property name="iconSize" > + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="getStatusButton" > + <property name="minimumSize" > + <size> + <width>0</width> + <height>29</height> + </size> + </property> + <property name="text" > + <string>get status</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="getCoordButton" > + <property name="minimumSize" > + <size> + <width>0</width> + <height>29</height> + </size> + </property> + <property name="text" > + <string>get coord</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QListView" name="objectsView" /> + </item> + </layout> + </widget> + </widget> + <action name="actionOpen" > + <property name="text" > + <string>&Open</string> + </property> + </action> + <action name="actionSave" > + <property name="text" > + <string>&Save</string> + </property> + </action> + <action name="actionSaveAs" > + <property name="text" > + <string>Save as...</string> + </property> + </action> + <action name="actionQuit" > + <property name="text" > + <string>&Quit</string> + </property> + </action> + <action name="actionPreferences" > + <property name="text" > + <string>Preferences</string> + </property> + </action> + </widget> + <resources> + <include location="../qt4/resources.qrc" /> + </resources> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/state_decoder.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +""" +state_decoder +============= + +Module for decoding the internal state of the HP3562A DSA, using the +GPIB command "DSBN" (Dump State BiNary). + +This file can be exectued as a python script. Use '-h' for more informations. +""" +from pygpibtoolkit.HP3562A import format_header, decode_float, decode_string, decode_header + +from pygpibtoolkit.HP3562A.enum_types import * + +HEADER = [("Measurement mode", EMEAS, 'h', 2), + ("Measurement 1", EMTYP, 'h', 2), + ("Measurement 2", EMTYP, 'h', 2), + ("Window type", EWINTYP, 'h', 2), + ("Force/Expon window 1", EFEXPW, 'h', 2), + ("Force/Expon window 2", EFEXPW, 'h', 2), + ("Average type", EAVGTYP, 'h', 2), + ("Overlap percentage", int, 'h', 2), + ("Number of averages", int, 'h', 2), + ("Sweep # of averages", int, 'h', 2), + ("Trigger type", ETRGTYP, 'h', 2), + ("Trigger slope", ETRGSLP, 'h', 2), + ("Preview type", EPRVTYP, 'h', 2), + ("Sample type", ESMPTYP, 'h', 2), + ("Range units chan 1", ERNGUNT, 'h', 2), + ("Range units chan 2", ERNGUNT, 'h', 2), + ("Range type 1", ERNGTYP, 'h', 2), + ("Range type 2", ERNGTYP, 'h', 2), + ("Input coupling 1", EINCPL, 'h', 2), + ("Input coupling 2", EINCPL, 'h', 2), + ("Source type", ESRCTYP, 'h', 2), + ("Chirp percent", int, 'h', 2), + ("Burst percent", int, 'h', 2), + ("Sweep direction", ESWPDIR, 'h', 2), + ("Sweep mode", ESWPMOD, 'h', 2), + ("Ext sample freq untis", EXAXIS, 'h', 2), + ("Bandwidth units", EXAXIS, 'h', 2), + ("Log span index", int, 'h', 2), + ("Log start index", int, 'h', 2), + ("Sweep rate units", EXAXIS, 'h', 2), + ("Auto gain ref chan", EDEMODCH, 'h', 2), + ("Demod channels", EDEMODCH, 'h', 2), + ("Demod type chan 1", EDEMOD, 'h', 2), + ("Demod type chan 2", EDEMOD, 'h', 2), + ("Source level units", EXAXIS, 'h', 2), + ("Source offset units", EXAXIS, 'h', 2), + ("Trigger level units", EXAXIS, 'h', 2), + ("Capt/thru length units", EXAXIS, 'h', 2), + ("EU label 1", str, 's', 6), + ("EU label 2", str, 's', 6), + ("Auto carrier on/off", bool, 'h', 2), + ("Time average on/off", bool, 'h', 2), + ("Auto/fixed resolution", bool, 'h', 2), + ("Auto gain on/off", bool, 'h', 2), + ("Auto/fixed integrate",bool, 'h', 2), + ("Fast average on/off", bool, 'h', 2), + ("Overload reject on/off", bool, 'h', 2), + ("Chan 1 float/ground", bool, 'h', 2), + ("Chan 2 float/ground", bool, 'h', 2), + ("Time throughput on/off", bool, 'h', 2), + ("Demodulation on/off", bool, 'h', 2), + ("EU/volts chan 1", bool, 'h', 2), + ("EU/volts chan 2", bool, 'h', 2), + ("Manual/auto arm", bool, 'h', 2), + ("Demod preview on/off", bool, 'h', 2), + ("Delete freq on/off", bool, 'h', 2), + ("Lin res Fstart pegged", bool, 'h', 2), + ("Swept Fstart pegged", bool, 'h', 2), + ("Force length chan 1", decode_float, None, 4), + ("Force length chan 2", decode_float, None, 4), + ("Expon time constant 1", decode_float, None, 4), + ("Expon time constant 2", decode_float, None, 4), + ("Sweep time", decode_float, None, 4), + ("Sweep rate", decode_float, None, 4), + ("Sweep resolution", decode_float, None, 4), + ("Sweep integrate time", decode_float, None, 4), + ("Auto gain level", decode_float, None, 4), + ("Auto gain limit", decode_float, None, 4), + ("Source level", decode_float, None, 4), + ("EU value chan 1", decode_float, None, 4), + ("EU value chan 2", decode_float, None, 4), + ("Trigger delay chan 1", decode_float, None, 4), + ("Trigger delay chan 2", decode_float, None, 4), + ("Integrate var thresh", decode_float, None, 4), + ("Capt/thru length", decode_float, None, 4), + ("Frequency span", decode_float, None, 4), + ("Time record length", decode_float, None, 4), + ("Frequency resolution", decode_float, None, 4), + ("Time resolution", decode_float, None, 4), + ("External sample rate", decode_float, None, 4), + ("Sample rate (actual)", decode_float, None, 4), + ("Range channel 1", decode_float, None, 4), + ("Range channel 2", decode_float, None, 4), + ("Preview time", decode_float, None, 4), + ("Trigger level", decode_float, None, 4), + ("Source dc offset", decode_float, None, 4), + ("Fixed sine frequency", decode_float, None, 8), + ("Start frequency", decode_float, None, 8), + ("Center frequency", decode_float, None, 8), + ("Sweep start", decode_float, None, 8), + ("Sweep end", decode_float, None, 8), + ("Carrier frequency", decode_float, None, 8), + ] + +def decode_state(data): + """ + Decode the data (as generated by the HP3562A DSA in response to a + "DSBN" command), and returns a dict (header) + + header is the dictionnary of the header of the dumped data block, + """ + header, idx = decode_header(data, HEADER) + return header + + +def main(): + import sys + import optparse + opt = optparse.OptionParser("A simple tool for tracing a dumped trace") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, read from stdin') + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + + options, argv = opt.parse_args(sys.argv) + + + if options.filename is None: + print "Can't deal stdin for now..." + sys.exit(1) + #try: + if 1: + header = decode_state(open(options.filename, 'rb').read()) + else: + #except Exception, e: + print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename + print e + sys.exit(1) + + print format_header(header, HEADER, 100) + +if __name__ == "__main__": + main() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/HP3562A/trace_decoder.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +""" +trace_decoder +============= + +Module for decoding a trace generated by the HP3562A DSA, using the +GPIB command "DDBN" (Dump Data BiNary). + +This file can be exectued as a python script. Use '-h' for more informations. +""" +import struct +import numpy +from pygpibtoolkit.HP3562A import format_header, decode_float, decode_string, decode_header, read_trace +from pygpibtoolkit.HP3562A.enum_types import * + + +HEADER = [ ("Display function", EDSP, 'h', 2), + ('Number of elements', int, 'h', 2), + ('Displayed elements', int, 'h', 2), + ('Number of averages', int, 'h', 2), + ('Channel selection', ECH, 'h', 2), + ('Overflow status', EOVR, 'h', 2), + ('Overlap percentage', int, 'h', 2), + ('Domain', EDOM, 'h', 2), + ('Volts peak/rms', EVLT, 'h', 2), + ('Amplitude units', EAMP, 'h', 2), + ('X axis units', EXAXIS, 'h', 2), + ('Auto math label', str, 's', 14), + ('Trace label', str, 's', 22), + ('EU label 1', str, 's', 6), + ('EU label 2', str, 's', 6), + ('Float/Interger', bool, 'h', 2), + ('Complex/Real', bool, 'h', 2), + ('Live/Recalled', bool, 'h', 2), + ('Math result', bool, 'h', 2), + ('Real/Complex input', bool, 'h', 2), + ('Log/Linear data', bool, 'h', 2), + ('Auto math', bool, 'h', 2), + ('Real time status', bool, 'h', 2), + ('Measurement mode', EMEAS, 'h', 2), + ('Window', EWIN, 'h', 2), + ('Demod type channel 1', EDEMOD, 'h', 2), + ('Demod type channel 2', EDEMOD, 'h', 2), + ('Demod active channel 1', bool, 'h', 2), + ('Demod active channel 2', bool, 'h', 2), + ('Average status', EAVG, 'h', 2), + ('Not used', int, 'hh', 4), + ('Samp freq/2 (real)', decode_float, None, 4), + ('Samp freq/2 (imag)', decode_float, None, 4), + ('Not used', decode_float, None, 4), + ('Delta X-axis', decode_float, None, 4), + ('Max range', decode_float, None, 4), + ('Start time value', decode_float, None, 4), + ('Expon wind const 1', decode_float, None, 4), + ('Expon wind const 2', decode_float, None, 4), + ('EU value chan 1', decode_float, None, 4), + ('EU value chan 2', decode_float, None, 4), + ('Trig delay chan 1', decode_float, None, 4), + ('Trig delay chan 2', decode_float, None, 4), + ('Start freq value', decode_float, None, 8), + ('Start data value', decode_float, None, 8), + ] + +def decode_trace(data, idx=0): + """ + Decode the data (as generated by the HP3562A DSA in response to a + "DDBN" command), and returns a couple (header, values). + + header is the dictionnary of the header of the dumped data block, + value is a numpy array holding the trace (vector of float or + complex values). + """ + header, idx = decode_header(data, HEADER, idx) + return header, read_trace(data, idx, header["Number of elements"]) + +def main(): + import sys + import optparse + opt = optparse.OptionParser("A simple tool for tracing a dumped trace") + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, read from stdin') + opt.add_option('-m', '--mode', default='binary', + dest='mode', + help='Dumping mode (may be "binary" [default], "ascii" or "ansi")', + ) + opt.add_option('-d', '--display-header', default=False, + action="store_true", + dest="displayheader", + help="Display the trace header") + opt.add_option('-P', '--noplot-trace', default=True, + action="store_false", + dest="plot", + help="Do not display the plot of the trace") + opt.add_option('-x', '--xmode', default='lin', + dest='xmode', + help='X coordinate mode (may be "lin" [default] or "log")') + opt.add_option('-y', '--ymode', default='lin', + dest='ymode', + help='Y coordinate mode (may be "lin" [default], "log" or "db")') + + options, argv = opt.parse_args(sys.argv) + + + if options.filename is None: + print "Can't deal stdin for now..." + sys.exit(1) + try: + header, data = decode_trace(open(options.filename, 'rb').read()) + except Exception, e: + print "ERROR: can't read %s an interpret it as a HP3562 trace"%options.filename + print e + sys.exit(1) + + if options.displayheader: + print format_header(header, HEADER, 100) + if options.plot: + f0 = header['Start freq value'] + dx = header['Delta X-axis'] + n = header['Number of elements'] + x = numpy.linspace(f0, f0+dx*n, len(data)) + y = data.copy() + + import pylab + if options.ymode != "lin": + minv = min(y[y>0]) + y[y==0] = minv + y = numpy.log10(y) + if options.ymode == "db": + y = y*10 + pylab.ylabel('db') + pylab.grid() + pylab.plot(x, y) + pylab.xlabel('frequency') + pylab.show() + + +if __name__ == "__main__": + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/gpib_plotter.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,138 @@ +import sys, os +import time +from pygpibtoolkit import pygpib as gpib +from pygpibtoolkit.prologix import GPIB + +class GPIBplotter(GPIB): + _replies={ + "OE": "0", + "OH": "0,0,10000,7500", + "OI": "7470A", + "OP": "0,0,10000,7500", + "OO": "0,1,0,0,0,0,0,0", + "OF": "40,40", + "OS": "24", + } + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=5): + super(GPIBplotter, self).__init__(device, baudrate, timeout, address, mode=0) + + def plotStarted(self): + pass + + def load_plot(self, wait_timeout=0): + """ + Make a full plot process. + + 'wait_timeout' is the first timeout. Same semantic as the + 'timeout' property of a serial connection (if set to None, it + will block until the GPIB device actually perform a plot + command.) + """ + res = "" + i=0 + replies = self._replies.copy() + if wait_timeout is not None and not isinstance(wait_timeout, (float, int)): + raise TypeError, "wait_timeout (%s:%s) has wrong type"%(repr(wait_timeout), type(wait_timeout)) + if wait_timeout<0: + raise ValueError + + self._cnx.timeout = wait_timeout + firstloop = True + newdata = False + while i<self._retries: + l = self._cnx.readline().strip() + if firstloop: + self._cnx.timeout = self._timeout + firstloop = False + self.plotStarted() + if l == "": + if i == 0:# > (self._retries/2): + # ie. we just received new stuffs (i is reset in the else block) + for k, v in replies.items(): + # check wether we should reply smthg + eres = res.replace('\n', '').strip() + if eres.endswith(k) or eres.endswith(k+';') or eres.endswith(k+';OE'): + self._cnx.write("%s"%v) + if k == "OS": + replies[k] = "16" + break + self._cnx.write('\r') + #time.sleep(0.1) + i += 1 + else: + if not res: + print "Plotting..." + res += l + '\n' + i = 0 + #time.sleep(0.1) + if res: + print "DONE (received %d characters)"%len(res) + return res + +if __name__ == '__main__': + import optparse + opt = optparse.OptionParser('A simple HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') + opt.add_option('-f', '--filename', default=None, + dest='filename', + help='Output filename. If not set, write to stdout') + opt.add_option('-d', '--device', default='/dev/ttyUSB0', + dest='device', + help='Device of the RS232 connection (default: /dev/ttyUSB0)', + ) + opt.add_option('-a', '--address', default=0, + dest='address', + help='GPIB address of the device', + ) + opt.add_option('-l', '--loop', default=False, + action="store_true", + dest="loop", + help="Continuously wait for new plots. If set, filename must be set and files will be created with _n at the end") + opt.add_option('-v', '--verbose', default=False, + action="store_true", + dest="verbose", + help="Verbose mode",) + + options, argv = opt.parse_args(sys.argv) + + if options.loop and not options.filename: + opt.error('If loop is set, you *must* provide a filename') + + if options.filename: + outf = open(options.filename, "w") + else: + outf = sys.stdout + + + try: + plotter = GPIBplotter(device=options.device, address=int(options.address), + timeout=0.06) + except (gpib.SerialException, gpib.ConnectionError), e: + sys.stderr.write('Connection error:\n\t' + '\n\t'.join([str(x) for x in e.args]) + '\n') + sys.stderr.write('Check your parameters\n') + sys.exit(1) + if options.verbose: + sys.stderr.write('connection established\n') + + loop = True + nloop = 0 + while loop: + plot = plotter.load_plot(wait_timeout=0.1) + if options.verbose: + sys.stderr.write('.') + if plot: + outf.write(plot) + if options.verbose: + sys.stderr.write('\n') + sys.stderr.write('Received a new plot (written to %s)\n'%outf.name) + if not options.loop: + loop = False + else: + nloop += 1 + fname, ext = os.path.splitext(options.filename) + outf = open(fname + "_%d"%nloop + ext, 'w') + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/gpib_plotter_mockup.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,35 @@ +import glob +import os +import time +import random + +class GPIBplotter:#(GPIB): + """ + A mockup thet will find in a directory some HPGL files and feed them randomly + """ + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=5): + self._timeout = timeout + self.filenames = glob.glob('examples/*.plt') + self._next = random.randint(10,50) + self._num = 0 + + def plotStarted(self): + pass + + def load_plot(self, wait_timeout=0): + if wait_timeout: + time.sleep(wait_timeout) + self._num += 1 + if self._num > self._next: + ret = open(random.choice(self.filenames)).read() + if len(ret)>0: + self.plotStarted() + self._num = 0 + self._next = random.randint(10,100) + time.sleep(random.randint(1,3)) + return ret + return None + return None + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/hpgl_mpl.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +import numpy +import pylab + +from hpgl_parser import HPGLParser + +class HPGLmplPlotter(HPGLParser): + pens = "krbgcmyw" + lines = ['-', ':', '--', '-.', '.', '-', '-', '-'] + def __init__(self, data): + super(HPGLmplPlotter, self).__init__(data) + pylab.show() + + def plot_symbols(self, points): + x, y = points.T + pylab.plot(x, y, 'o') + + def plot_lines(self, points): + x, y = points.T + pylab.plot(x, y, self.pens[self.pen]+self.lines[self.line_type]) + +if __name__ == "__main__": + import sys + data = open(sys.argv[1]).read() + HPGLmplPlotter(data) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/hpgl_parser.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,451 @@ +# -*- coding: utf-8 -*- + +import re +import numpy + +vsplitter = re.compile('[ ,]') +vextractor = re.compile('(?P<value>[^;\n\r\aA-DF-Za-df-z]*?)(?P<terminator>[;\n\r\a]+)', re.S+re.M) + +class HPGLParser(object): + def __init__(self, data=None): + self.str_terminator = chr(0x03) + self.IN() + if data: + self.parse(data) + + def parse(self, data): + self.data = data + self.idx = 0 + self.IN() + while self.idx<len(data): + while data[self.idx] in [';','\n','\r', '\a', ' ']: + self.idx += 1 + if data[self.idx] == chr(0x03): + self.ESC() + else: + cmd = data[self.idx: self.idx+2].upper() + self.idx += 2 + getattr(self, cmd)() + while self.idx<len(data) and data[self.idx] in [';','\n','\r', '\a', ' ']: + self.idx += 1 + + def user_to_abs(self, pos): + """ + converts a position (x, y) given in user units to absolute + units + """ + return pos #TODO + + def extract_value(self, raw=False): + m = vextractor.match(self.data[self.idx:]) + if m: + val = m.group("value") + self.idx = self.idx + len(val) + len(m.group("terminator")) + val = vsplitter.split(val) + if not raw: + val = [eval(x) for x in val if x.strip()] + return val + return [] + + def extract_string(self): + idx2 = self.data.find(self.str_terminator, self.idx) + val = self.data[self.idx: idx2] + self.idx = idx2+1 + return val + + def polyline(self, values): + if (len(values)%2) == 1: + # this is a syntax error in the HPGL data + values = values[:-1] + values = numpy.array(values).reshape(-1, 2) + # TODO use scaling + # do plots + values = numpy.r_[[self.pos], values] #add self.pos as 1st value + if self.plot_mode == 'relative': + values = values.cumsum(axis=0) + if self.symbol_mode: + self.plot_symbols(values[1:]) + if self.pen_state == "down": + self.plot_lines(values) + self.pos = values[-1] + + def plot_string(self, s): + pass + + def get_window_for_paper(self): + return 0,100,0,100 + def get_scaling_points_for_paper(self): + return 0,100,0,100 + + # effective plot methods; to be defined in subclasses (backends) + def plot_symbols(self, points): + pass + def plot_lines(self, points): + pass + + # HPGL-related methods + def ESC(self): + #print "ESC" + self.idx += 1 + + def OE(self): + """ Output Error """ + pass + + def EC(self): + """? what's this """ + values = self.extract_value() + + def OO(self): + """idem""" + pass + + def OP(self): + """idem""" + pass + + def VS(self): + """idem""" + pass + + + def DF(self): + """ Default """ + self.pen = 0 + self.pen_width = 1 # 1/300 inch + self.pos = 0,0 + self.char_set = "std" + self.plot_mode = 'absolute' + self.char_direction = 1,0 + self.line_type = 0 # 'solid' + self.pattern_len = 4 #percentage + self.window = self.get_window_for_paper() + self.char_size = 0.75, 1.5 #percentages + self.symbol_mode = False + self.tick_len = 0.5, 0.5 # % + self.std_char_set = 0 + self.alt_char_set = 0 + self.selected_char_set = 0 + self.char_slant = 0 # degrees + self.scale = None + self.str_terminator = chr(0x03) + self.chord_ang = 5 #degrees + self.fill_type = 'bidirectionnal', 1 + self.fill_distance = 1 # % + self.fill_slant = 0 # degrees + self.pen_thickness = 0.3 # mm + + def IN(self): + """ Initialize """ + self.DF() + self.pen_state = "up" + self.rotation = 0 #degrees + self.scaling_points = self.get_scaling_points_for_paper() + + def IP(self): + """ Input Scale Point""" + values = self.extract_value() + if len(values) == 0: + self.scaling_points = self.get_scaling_points_for_paper() + elif len(values) == 2: + p1x, p1y, p2x, p2y = self.scaling_points + dx, dy = p2x-p1x, p2y-p1y + x, y = values + self.scaling = (x, y, x+dx, y+dy) + elif len(values) == 4: + self.values = tuple(values) + + def SC(self): + """ Scale """ + values = self.extract_value() + if len(values) == 4: + self.scale = tuple(values) + else: + self.scale = None + + def IW(self): + values = self.extract_value() + if len(values) == 0: + self.window = self.get_window_for_paper() + elif len(value) == 4: + self.window = tuple(values) + + def RO(self): + values = self.extract_value() + if len(values) == 0: + self.rotation = 0 + elif len(values) == 1: + self.rotation = values[0] + + def PG(self): + pass + + def PU(self): + """ Pen Up""" + self.pen_state = "up" + values = self.extract_value() + self.polyline(values) + + def PD(self): + """ Pen Down """ + self.pen_state = "down" + values = self.extract_value() + self.polyline(values) + + def PA(self): + """ Plot Absolute """ + self.plot_mode = "absolute" + values = self.extract_value() + self.polyline(values) + + def PR(self): + """ Plot Relative """ + self.plot_mode = "relative" + values = self.extract_value() + self.polyline(values) + + def AA(self): + """ Arc Absolute """ + values = self.extract_value() + if len(values) in [3, 4]: + x, y, qc = values[:3] + if len(values)==4: + qd = values[-1] + else: + qd = self.chord_ang + # TODO : plot arc + print "plotting an arc" + def AR(self): + """ Arc Relative """ + values = self.extract_value() + # TODO + + def CI(self): + """ Circle Plot""" + values = self.extract_value() + # TODO + + def EA(self): + """Edge Rectangle Absolute""" + values = self.extract_value() + # TODO + + def ER(self): + """Edge Rectangle Relative""" + values = self.extract_value() + # TODO + + def EW(self): + """ Edge Wedge """ + values = self.extract_value() + # TODO + + def RA(self): + """ Fill Rectangle Absolute """ + values = self.extract_value() + # TODO + + def RR(self): + """ Fill Rectangle Relative """ + values = self.extract_value() + # TODO + + def WG(self): + """ Fill Wedge """ + values = self.extract_value() + # TODO + + def FT(self): + """ Fill Type """ + values = self.extract_value() + # TODO + + def LT(self): + """ Line Type """ + values = self.extract_value() + + if len(values)==0: + self.line_type = 0 #'solid' + else: + self.line_type = values[0] + if len(values)>1: + self.pattern_len = values[1] + + def PW(self): + """ Pen Width """ + values = self.extract_value() + if len(values) == 1: + self.pen_thickness = values[0] + + def SM(self): + """ Symbol Mode """ + values = self.extract_value() + if len(values) == 0: + self.symbol_mode = False + elif len(values) == 1: + self.symbol_mode = values[0] + + def SP(self): + """ Select Pen """ + values = self.extract_value() + if len(values) == 1: + self.pen = values[0] + + def TL(self): + """ Tick Len """ + values = self.extract_value() + # TODO + + def XT(self): + """ X-axis Tick """ + # TODO + print "X ticks" + + def YT(self): + """ X-axis Tick """ + # TODO + print "Y ticks" + + def PT(self): + """ Pen Thickness Select """ + values = self.extract_value() + if len(values) == 0: + self.pen_thickness = 0.3 + elif len(values) == 1: + self.pen_thickness = values[0] + + def CS(self): + """ Standard Character Set """ + values = self.extract_value() + if len(values) == 0: + self.std_char_set = 0 + elif len(values) == 1: + self.std_char_set = values[0] + + def CA(self): + """ Alternate Character Set """ + values = self.extract_value() + if len(values) == 0: + self.alt_char_set = 0 + elif len(values) == 1: + self.alt_char_set = values[0] + + def SS(self): + """ Select Standard Character Set """ + self.char_set = "std" + + def SA(self): + """ Select Alternate Character Set """ + self.char_set = "alt" + + def DT(self): + """ Define Label Terminator """ + values = self.extract_value(raw=True) + if len(values) == 0: + self.str_terminator = chr(0x03) + elif len(values) == 1: + self.str_terminator = values[0] + + def LB(self): + """ Character Plot """ + values = self.extract_string() + self.plot_string(values) + x, y = self.pos + values = values.split('\n') + if self.char_size == "absolute": + x += len(values[-1]) * self.char_width *1016/2.54 + y += (len(values)-1) * self.char_height *1016/2.54 + else: + x0, x1, y0, y1 = self.scale + dx = x1-x0 + dy = y1-y0 + x += len(values[-1]) * self.char_width / 100.0 * dx + y += (len(values)-1) * self.char_height / 100.0 * dy + + #print "LB pos[%s] %s -> %s"%(repr(values), self.pos, (x,y)) + self.pos = [x, y] + + def get_char_size(self): + if self.char_size == "absolute": + x = self.char_width *1016/2.54 + y = self.char_height *1016/2.54 + else: + x0, x1, y0, y1 = self.scale + dx = x1-x0 + dy = y1-y0 + x = self.char_width / 100.0 * dx + y = self.char_height / 100.0 * dy + return x, y + + def DI(self): + """ Absolute Direction """ + values = self.extract_value() + if len(values) == 0: + self.char_direction = 1.0, 0.0 + elif len(values) == 2: + self.char_direction = values + + def DR(self): + """ Relative Direction """ + values = self.extract_value() + if len(values) == 0: + self.char_direction = 1.0, 0.0 + elif len(values) == 2: + # TODO : compute as percentages + self.char_direction = values + + def CP(self): + """ Character Plot """ + values = self.extract_value() + # TODO + if len(values) == 0: + values = 0, 1 + x, y = self.pos + if self.char_size == "absolute": + x += values[0] * self.char_width *1016/2.54 + y += values[1] * self.char_height *1016/2.54 + else: + x0, x1, y0, y1 = self.scale + dx = x1-x0 + dy = y1-y0 + x += values[0] * self.char_width / 100.0 * dx + y += values[1] * self.char_height / 100.0 * dy + #print "CB pos[%s] %s -> %s"%(repr(values), self.pos, (x,y)) + self.pos = [x, y] + + def SI(self): + """ Set Absolute Character Size """ + values = self.extract_value() + self.char_size = "absolute" + if len(values) == 0: + self.char_width = 0.1879 # cm + self.char_height = 0.2690 # cm + elif len(values) == 2: + self.char_width, self.char_height = values + + def SR(self): + """ Set Relative Character Size """ + values = self.extract_value() + self.char_size = "relative" + if len(values) == 0: + self.char_width = 0.75 # percentage + self.char_height = 1.5 # id + elif len(values) == 2: + self.char_width, self.char_height = values + + def SL(self): + """ Character Slant """ + values = self.extract_value() + # TODO + + def UC(self): + """ User Defined Character """ + values = self.extract_value() + # TODO + +if __name__ == '__main__': + import sys + data = open(sys.argv[1]).read() + + p = HPGLParser(data) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/hpgl_plotter_rc.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,1318 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created: sam. janv. 26 11:14:20 2008 +# by: The Resource Compiler for PyQt (Qt v4.3.2) +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore + +qt_resource_data = "\ +\x00\x00\x14\x97\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ +\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ +\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ +\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ +\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ +\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ +\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ +\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ +\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ +\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ +\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ +\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ +\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ +\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ +\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ +\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ +\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ +\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ +\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ +\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ +\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x35\ +\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\x38\ +\x2e\x32\x35\x32\x35\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\x73\x76\ +\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\x0a\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\x73\x69\ +\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\x20\x73\ +\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\x65\x3d\ +\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\x6c\x65\ +\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\x2f\x69\ +\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ +\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\x5f\x72\ +\x65\x64\x5f\x6f\x66\x66\x2e\x73\x76\x67\x22\x0a\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\x75\x74\x5f\x65\ +\x78\x74\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\x67\x2e\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\x74\x2e\x73\x76\ +\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\ +\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x3e\x0a\x20\x20\ +\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x64\ +\x65\x66\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\ +\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\ +\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\ +\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\x20\x20\x20\ +\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\ +\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\ +\x70\x34\x31\x31\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\ +\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\ +\x31\x32\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\ +\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\ +\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\ +\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\ +\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ +\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\ +\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\ +\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\ +\x74\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\ +\x64\x3d\x22\x73\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\ +\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x61\x61\x30\x30\x30\x30\x3b\x73\ +\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\ +\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x73\x74\x6f\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\ +\x20\x20\x3c\x2f\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\ +\x6e\x74\x3e\x0a\x20\x20\x20\x20\x3c\x72\x61\x64\x69\x61\x6c\x47\ +\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\ +\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\ +\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x61\x64\x69\ +\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x34\x32\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x63\x78\x3d\x22\x33\x32\x37\x2e\x34\ +\x31\x39\x32\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x63\x79\x3d\ +\x22\x33\x34\x2e\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x66\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\x32\x35\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x66\x79\x3d\x22\x33\x34\x2e\ +\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x72\ +\x3d\x22\x31\x33\x30\x2e\x31\x33\x32\x34\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\ +\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ +\x74\x54\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\ +\x69\x78\x28\x32\x2e\x35\x36\x39\x35\x37\x30\x37\x65\x2d\x37\x2c\ +\x2d\x33\x2e\x32\x34\x36\x39\x38\x35\x38\x2c\x32\x2e\x32\x39\x36\ +\x32\x34\x35\x2c\x36\x2e\x38\x37\x30\x38\x39\x36\x39\x65\x2d\x36\ +\x2c\x33\x36\x36\x2e\x39\x31\x30\x34\x39\x2c\x31\x35\x30\x34\x2e\ +\x31\x38\x33\x29\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\ +\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\ +\x6c\x65\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\ +\x22\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ +\x34\x31\x31\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\ +\x31\x32\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\ +\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\ +\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\ +\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\ +\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\ +\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\ +\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\ +\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\ +\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\ +\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\ +\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ +\x74\x55\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\ +\x65\x4f\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\ +\x31\x3d\x22\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\ +\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\ +\x38\x38\x2e\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x79\x32\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\ +\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ +\x28\x31\x2e\x32\x34\x35\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\ +\x2e\x32\x36\x31\x35\x38\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\ +\x36\x38\x2c\x2d\x39\x30\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\ +\x2f\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\ +\x65\x77\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\ +\x22\x0a\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\ +\x3d\x22\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\ +\x62\x6f\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\ +\x36\x36\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\ +\x72\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ +\x65\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ +\x65\x73\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\ +\x30\x2e\x39\x36\x31\x36\x34\x36\x31\x31\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x32\ +\x36\x2e\x33\x38\x33\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x36\x39\x2e\x37\x31\ +\x35\x34\x39\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\ +\x70\x65\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\ +\x73\x3d\x22\x70\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\ +\x65\x72\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ +\x2d\x77\x69\x64\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\ +\x77\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\ +\x64\x6f\x77\x2d\x78\x3d\x22\x31\x31\x22\x0a\x20\x20\x20\x20\x20\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ +\x79\x3d\x22\x31\x33\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\ +\x61\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\ +\x65\x74\x61\x64\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\ +\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\ +\x63\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\ +\x74\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\ +\x2f\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\ +\x75\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\ +\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\ +\x65\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\ +\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\ +\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\ +\x0a\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\ +\x20\x3c\x67\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\ +\x65\x3a\x6c\x61\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\ +\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ +\x3a\x67\x72\x6f\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\ +\x72\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\ +\x72\x31\x22\x0a\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\ +\x72\x6d\x3d\x22\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\ +\x37\x35\x2e\x39\x35\x38\x34\x37\x2c\x2d\x31\x37\x31\x2e\x37\x39\ +\x38\x39\x34\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\ +\x33\x39\x37\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\ +\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\ +\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x33\x31\x33\ +\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\ +\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\ +\x75\x72\x6c\x28\x23\x72\x61\x64\x69\x61\x6c\x47\x72\x61\x64\x69\ +\x65\x6e\x74\x33\x31\x34\x32\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\ +\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\ +\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x77\x69\x64\x74\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\ +\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\ +\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\ +\x30\x36\x36\x36\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\ +\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\ +\x69\x74\x79\x3a\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\ +\x65\x63\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\ +\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\ +\x3a\x23\x61\x32\x30\x30\x30\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\ +\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\ +\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x77\x69\x64\x74\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\ +\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\ +\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\ +\x30\x36\x36\x36\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\ +\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\ +\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\ +\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x30\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\ +\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\ +\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\ +\x32\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\ +\x30\x2e\x39\x33\x39\x37\x34\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\ +\x3c\x72\x65\x63\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\ +\x31\x37\x36\x2e\x33\x38\x37\x34\x34\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x78\x3d\x22\x31\x38\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\ +\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x77\x69\x64\x74\x68\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\ +\x32\x31\x36\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\ +\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\ +\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\ +\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\ +\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\ +\x23\x39\x35\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\ +\x69\x64\x74\x68\x3a\x39\x2e\x31\x37\x37\x30\x30\x30\x30\x35\x3b\ +\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\ +\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\ +\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\ +\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\ +\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\ +\x3a\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\ +\x69\x6c\x6c\x3a\x75\x72\x6c\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\ +\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\ +\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\ +\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\ +\x72\x6f\x6b\x65\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\ +\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\ +\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\ +\x22\x4d\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\ +\x2e\x39\x33\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\ +\x35\x2c\x33\x35\x32\x2e\x35\x36\x32\x35\x20\x43\x20\x31\x39\x30\ +\x2e\x39\x33\x37\x35\x32\x2c\x33\x35\x32\x2e\x37\x39\x39\x38\x32\ +\x20\x31\x39\x36\x2e\x38\x33\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\ +\x33\x37\x35\x20\x32\x30\x32\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\ +\x32\x2e\x39\x33\x37\x35\x20\x43\x20\x33\x31\x32\x2e\x38\x30\x30\ +\x31\x34\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\x34\x30\x36\x2e\ +\x39\x35\x30\x34\x39\x2c\x33\x31\x31\x2e\x36\x31\x30\x34\x34\x20\ +\x34\x34\x35\x2e\x30\x36\x32\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\ +\x4c\x20\x34\x34\x35\x2e\x30\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\ +\x33\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\ +\x31\x38\x30\x2e\x39\x33\x37\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x34\ +\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\ +\x79\x70\x65\x3d\x22\x61\x72\x63\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\ +\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\ +\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\ +\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\ +\x6f\x6b\x65\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\ +\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\ +\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\ +\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\ +\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\ +\x34\x36\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\ +\x6f\x64\x69\x3a\x63\x78\x3d\x22\x33\x30\x33\x2e\x36\x34\x36\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\ +\x3a\x63\x79\x3d\x22\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x72\x78\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\ +\x64\x69\x70\x6f\x64\x69\x3a\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\ +\x31\x38\x35\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\ +\x4d\x20\x33\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\ +\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\ +\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\ +\x2c\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\x20\x37\ +\x2e\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\x20\x20\ +\x33\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\ +\x34\x20\x7a\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\ +\x2f\x73\x76\x67\x3e\x0a\ +\x00\x00\x14\x96\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ +\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ +\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ +\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ +\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ +\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ +\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ +\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ +\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ +\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ +\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ +\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ +\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ +\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ +\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ +\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ +\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ +\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ +\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ +\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ +\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x34\ +\x31\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\ +\x38\x2e\x32\x35\x32\x34\x31\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ +\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ +\x69\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\ +\x0a\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\ +\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\ +\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\ +\x65\x3d\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\ +\x6c\x65\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\ +\x2f\x69\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\ +\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\ +\x5f\x67\x72\x65\x65\x6e\x2e\x73\x76\x67\x22\x0a\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\x75\x74\x5f\x65\ +\x78\x74\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\x67\x2e\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\x74\x2e\x73\x76\ +\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\ +\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x3e\x0a\x20\x20\ +\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x64\ +\x65\x66\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\ +\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\ +\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\ +\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\x20\x20\x20\ +\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\ +\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\ +\x70\x34\x31\x31\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\ +\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\ +\x31\x32\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\ +\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\ +\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\ +\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\ +\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ +\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\ +\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\ +\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\ +\x74\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\ +\x64\x3d\x22\x73\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\ +\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x30\x30\x66\x66\x30\x30\x3b\x73\ +\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\ +\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x73\x74\x6f\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\ +\x20\x20\x3c\x2f\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\ +\x6e\x74\x3e\x0a\x20\x20\x20\x20\x3c\x72\x61\x64\x69\x61\x6c\x47\ +\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\ +\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\ +\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x61\x64\x69\ +\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x34\x32\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x63\x78\x3d\x22\x33\x32\x37\x2e\x34\ +\x31\x39\x32\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x63\x79\x3d\ +\x22\x33\x34\x2e\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x66\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\x32\x35\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x66\x79\x3d\x22\x33\x34\x2e\ +\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x72\ +\x3d\x22\x31\x33\x30\x2e\x31\x33\x32\x34\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\ +\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ +\x74\x54\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\ +\x69\x78\x28\x32\x2e\x35\x36\x39\x35\x37\x30\x37\x65\x2d\x37\x2c\ +\x2d\x33\x2e\x32\x34\x36\x39\x38\x35\x38\x2c\x32\x2e\x32\x39\x36\ +\x32\x34\x35\x2c\x36\x2e\x38\x37\x30\x38\x39\x36\x39\x65\x2d\x36\ +\x2c\x33\x36\x36\x2e\x39\x31\x30\x34\x39\x2c\x31\x35\x30\x34\x2e\ +\x31\x38\x33\x29\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\ +\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\ +\x6c\x65\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\ +\x22\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ +\x34\x31\x31\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\ +\x31\x32\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\ +\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\ +\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\ +\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\ +\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\ +\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\ +\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\ +\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\ +\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\ +\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\ +\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\ +\x74\x55\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\ +\x65\x4f\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\ +\x31\x3d\x22\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\ +\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\ +\x38\x38\x2e\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x79\x32\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\ +\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ +\x28\x31\x2e\x32\x34\x35\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\ +\x2e\x32\x36\x31\x35\x38\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\ +\x36\x38\x2c\x2d\x39\x30\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\ +\x2f\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\ +\x65\x77\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\ +\x22\x0a\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\ +\x3d\x22\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\ +\x62\x6f\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\ +\x36\x36\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\ +\x72\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ +\x65\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\ +\x65\x73\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\ +\x30\x2e\x34\x38\x30\x38\x32\x33\x30\x36\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x37\ +\x32\x2e\x30\x34\x37\x32\x34\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x32\x30\x37\x2e\x31\ +\x32\x31\x33\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\ +\x70\x65\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\ +\x73\x3d\x22\x70\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\ +\x65\x72\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ +\x2d\x77\x69\x64\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\ +\x77\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\ +\x64\x6f\x77\x2d\x78\x3d\x22\x32\x35\x22\x0a\x20\x20\x20\x20\x20\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ +\x79\x3d\x22\x34\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\ +\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\ +\x74\x61\x64\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\ +\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\ +\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\ +\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\ +\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\ +\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\ +\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\ +\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\x3e\ +\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\ +\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\x20\ +\x3c\x67\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ +\x3a\x6c\x61\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\x31\ +\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\ +\x67\x72\x6f\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\x72\ +\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\x72\ +\x31\x22\x0a\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\ +\x6d\x3d\x22\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\x37\ +\x35\x2e\x39\x35\x38\x35\x32\x2c\x2d\x31\x37\x31\x2e\x37\x39\x38\ +\x39\x39\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x30\x30\ +\x66\x66\x30\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ +\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ +\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\ +\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ +\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ +\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ +\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ +\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\ +\x63\x74\x32\x31\x36\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\ +\x69\x64\x74\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ +\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\ +\x39\x37\x34\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\ +\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x37\x36\x2e\ +\x33\x38\x37\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\ +\x22\x31\x38\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x36\x39\x2e\x30\ +\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x32\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\ +\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\ +\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\ +\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\x63\ +\x34\x30\x62\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\ +\x3a\x39\x2e\x31\x37\x36\x38\x39\x38\x39\x36\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\ +\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\ +\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\ +\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x20\ +\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\x39\x37\x34\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\ +\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\ +\x69\x67\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x32\ +\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x33\x31\x33\x34\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\ +\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x75\x72\x6c\x28\ +\x23\x72\x61\x64\x69\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\x33\ +\x31\x34\x32\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ +\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ +\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\ +\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ +\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ +\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ +\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ +\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ +\x6c\x6c\x3a\x75\x72\x6c\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\ +\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\x6c\ +\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\ +\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\ +\x6f\x6b\x65\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\ +\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\ +\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\ +\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\ +\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\ +\x4d\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\ +\x39\x33\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\ +\x2c\x33\x35\x32\x2e\x35\x36\x32\x35\x20\x43\x20\x31\x39\x30\x2e\ +\x39\x33\x37\x35\x32\x2c\x33\x35\x32\x2e\x37\x39\x39\x38\x32\x20\ +\x31\x39\x36\x2e\x38\x33\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\x33\ +\x37\x35\x20\x32\x30\x32\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\x32\ +\x2e\x39\x33\x37\x35\x20\x43\x20\x33\x31\x32\x2e\x38\x30\x30\x31\ +\x34\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\x34\x30\x36\x2e\x39\ +\x35\x30\x34\x39\x2c\x33\x31\x31\x2e\x36\x31\x30\x34\x34\x20\x34\ +\x34\x35\x2e\x30\x36\x32\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\x4c\ +\x20\x34\x34\x35\x2e\x30\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\x33\ +\x37\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\ +\x38\x30\x2e\x39\x33\x37\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x34\x22\ +\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\x79\ +\x70\x65\x3d\x22\x61\x72\x63\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\ +\x3b\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\ +\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\ +\x6b\x65\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\ +\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\ +\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\ +\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\ +\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\ +\x36\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\ +\x64\x69\x3a\x63\x78\x3d\x22\x33\x30\x33\x2e\x36\x34\x36\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x63\x79\x3d\x22\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x72\ +\x78\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\ +\x69\x70\x6f\x64\x69\x3a\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\x31\ +\x38\x35\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\ +\x20\x33\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\ +\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\x33\ +\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\x2c\ +\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\ +\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\ +\x30\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\ +\x20\x7a\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\ +\x73\x76\x67\x3e\x0a\ +\x00\x00\x10\xa0\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ +\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ +\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ +\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ +\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ +\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ +\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ +\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ +\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ +\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ +\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ +\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ +\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ +\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ +\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ +\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ +\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ +\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ +\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ +\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ +\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x34\ +\x31\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\ +\x38\x2e\x32\x35\x32\x34\x31\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ +\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ +\x69\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\ +\x0a\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\ +\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\ +\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\ +\x65\x3d\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\ +\x6c\x65\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\ +\x2f\x69\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\ +\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\ +\x5f\x67\x72\x65\x65\x6e\x5f\x6f\x66\x66\x2e\x73\x76\x67\x22\x0a\ +\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\ +\x75\x74\x5f\x65\x78\x74\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\ +\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\ +\x74\x2e\x73\x76\x67\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\ +\x20\x20\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\ +\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\ +\x64\x3d\x22\x64\x65\x66\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\ +\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\ +\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\ +\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\ +\x63\x6f\x6c\x6f\x72\x3a\x23\x61\x63\x61\x63\x61\x63\x3b\x73\x74\ +\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x2e\x36\x38\x37\ +\x38\x33\x30\x36\x39\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\x31\x31\ +\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\ +\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\ +\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\ +\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\ +\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\ +\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\x31\x32\x31\x22\ +\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\x6e\x65\x61\x72\ +\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\ +\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\ +\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\ +\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\ +\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\ +\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\ +\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\ +\x6f\x72\x3a\x23\x30\x30\x66\x66\x30\x30\x3b\x73\x74\x6f\x70\x2d\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\ +\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\ +\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\ +\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\ +\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\x22\x61\x6c\x77\ +\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x6c\x69\x6e\ +\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\x65\x61\x72\x47\ +\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\ +\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\ +\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\x32\x36\x30\ +\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\ +\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\x36\x35\x39\ +\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\x3d\x22\x33\ +\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\x72\x61\x6e\x73\x66\x6f\ +\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\x31\x2e\x32\x34\x35\ +\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\x2e\x32\x36\x31\x35\x38\ +\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\x36\x38\x2c\x2d\x39\x30\ +\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\x2f\x3e\x0a\x20\x20\x3c\ +\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\x73\x6f\x64\x69\x70\x6f\ +\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\x0a\x20\x20\x20\ +\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\x22\x0a\x20\x20\x20\x20\ +\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x66\x66\x66\ +\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\ +\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\x36\x36\x36\x36\x22\x0a\ +\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x6f\x70\x61\x63\x69\ +\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x6f\x70\x61\x63\x69\ +\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x73\x68\x61\x64\x6f\ +\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\x30\x2e\x34\x38\x30\x38\ +\x32\x33\x30\x36\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x37\x32\x2e\x30\x34\x37\x32\ +\x34\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ +\x3a\x63\x79\x3d\x22\x2d\x36\x34\x2e\x37\x39\x33\x36\x39\x36\x22\ +\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x64\ +\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\x73\x3d\x22\x70\ +\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ +\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\x65\x72\x3d\x22\ +\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x77\x69\x64\ +\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x68\x65\ +\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\x20\x20\x20\x20\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ +\x78\x3d\x22\x32\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x79\x3d\x22\x32\ +\x31\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\x64\x61\x74\ +\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\x74\x61\x64\ +\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\ +\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\x63\x3a\x57\ +\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\ +\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x69\x6d\ +\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\x64\x63\x3a\ +\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\x72\x63\x65\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ +\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\x2f\x53\x74\ +\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\ +\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\x3e\x0a\x20\x20\ +\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x3c\ +\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\x20\x3c\x67\x0a\ +\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x6c\x61\ +\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\x31\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x67\x72\x6f\ +\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\x72\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\ +\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\ +\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\x37\x35\x2e\x39\ +\x35\x38\x35\x32\x2c\x2d\x31\x37\x31\x2e\x37\x39\x38\x39\x39\x29\ +\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\ +\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x30\x30\x39\x39\x30\ +\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\ +\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\ +\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\x66\x66\x66\ +\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x30\ +\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\ +\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\ +\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x2c\x20\x30\ +\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\ +\x31\x36\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x35\x39\ +\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\ +\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\x39\x37\x34\ +\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x37\x36\x2e\x33\x38\x37\ +\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\ +\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\ +\x32\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x32\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\x61\ +\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\ +\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\ +\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\ +\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\x38\x33\x30\x34\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x39\x2e\ +\x31\x37\x36\x38\x39\x38\x39\x36\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\ +\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\ +\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\ +\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x20\x2f\x3e\x0a\ +\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x75\x72\x6c\ +\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ +\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\ +\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\ +\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x6e\x6f\ +\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\ +\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\ +\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\ +\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x31\x38\x35\x2e\ +\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\x35\x20\x4c\ +\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x33\x35\x32\x2e\x35\ +\x36\x32\x35\x20\x43\x20\x31\x39\x30\x2e\x39\x33\x37\x35\x32\x2c\ +\x33\x35\x32\x2e\x37\x39\x39\x38\x32\x20\x31\x39\x36\x2e\x38\x33\ +\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\x32\x30\x32\ +\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\ +\x43\x20\x33\x31\x32\x2e\x38\x30\x30\x31\x34\x2c\x33\x35\x32\x2e\ +\x39\x33\x37\x35\x20\x34\x30\x36\x2e\x39\x35\x30\x34\x39\x2c\x33\ +\x31\x31\x2e\x36\x31\x30\x34\x34\x20\x34\x34\x35\x2e\x30\x36\x32\ +\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\x4c\x20\x34\x34\x35\x2e\x30\ +\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\x35\x20\x4c\x20\x31\ +\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\ +\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x70\x61\x74\x68\x33\x31\x34\x34\x22\x20\x2f\x3e\x0a\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\x20\x20\x73\ +\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\x79\x70\x65\x3d\x22\x61\x72\ +\x63\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\ +\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\ +\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ +\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ +\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\ +\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ +\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\ +\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\ +\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x6e\x6f\x6e\ +\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\ +\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\ +\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ +\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x36\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x63\x78\x3d\ +\x22\x33\x30\x33\x2e\x36\x34\x36\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x63\x79\x3d\x22\x31\x32\ +\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x72\x78\x3d\x22\x30\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\x31\x38\x35\x33\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x33\x30\x33\x2e\x36\ +\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\ +\x20\x37\x2e\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\ +\x20\x20\x33\x30\x33\x2e\x36\x34\x36\x2c\x31\x32\x39\x2e\x39\x38\ +\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\ +\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\ +\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x7a\x22\x20\x2f\x3e\ +\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ +\x00\x00\x14\x96\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ +\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ +\x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x43\x72\x65\x61\x74\ +\x65\x64\x20\x77\x69\x74\x68\x20\x49\x6e\x6b\x73\x63\x61\x70\x65\ +\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x29\x20\x2d\x2d\x3e\x0a\ +\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x64\x63\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\ +\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\x73\x2f\x31\x2e\ +\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x65\x62\x2e\x72\x65\x73\x6f\ +\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x63\x63\x2f\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\ +\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\ +\x74\x61\x78\x2d\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ +\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\ +\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\ +\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ +\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ +\x78\x6c\x69\x6e\x6b\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ +\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\ +\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\ +\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\ +\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\ +\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\ +\x20\x77\x69\x64\x74\x68\x3d\x22\x32\x37\x38\x2e\x32\x35\x32\x34\ +\x31\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x37\ +\x38\x2e\x32\x35\x32\x34\x31\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ +\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\ +\x69\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x33\x32\x22\ +\x0a\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\ +\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x34\x35\x2e\x31\x22\x0a\x20\x20\ +\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x62\x61\x73\ +\x65\x3d\x22\x2f\x68\x6f\x6d\x65\x2f\x64\x61\x76\x69\x64\x2f\x45\ +\x6c\x65\x63\x74\x72\x6f\x6e\x69\x63\x2f\x48\x50\x33\x35\x36\x32\ +\x2f\x69\x63\x6f\x6e\x73\x22\x0a\x20\x20\x20\x73\x6f\x64\x69\x70\ +\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x6c\x65\x64\ +\x5f\x72\x65\x64\x2e\x73\x76\x67\x22\x0a\x20\x20\x20\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x3a\x6f\x75\x74\x70\x75\x74\x5f\x65\x78\x74\ +\x65\x6e\x73\x69\x6f\x6e\x3d\x22\x6f\x72\x67\x2e\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x2e\x6f\x75\x74\x70\x75\x74\x2e\x73\x76\x67\x2e\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\x65\x72\ +\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x3e\x0a\x20\x20\x3c\x64\ +\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x64\x65\x66\ +\x73\x34\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\ +\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\ +\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\ +\x65\x6e\x74\x34\x31\x31\x37\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\ +\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x30\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\ +\x31\x31\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x73\ +\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\ +\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\ +\x66\x66\x66\x66\x66\x66\x3b\x73\x74\x6f\x70\x2d\x6f\x70\x61\x63\ +\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x74\x6f\x70\x34\x31\x32\ +\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6c\x69\x6e\x65\ +\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x3e\x0a\x20\x20\x20\x20\ +\x3c\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x61\ +\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\ +\x2d\x63\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x66\x66\x66\x66\x3b\x73\ +\x74\x6f\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\ +\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x73\x74\x6f\x70\x33\x31\x33\x38\x22\x20\x2f\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x3c\x73\x74\x6f\x70\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x73\x74\x6f\x70\x2d\x63\ +\x6f\x6c\x6f\x72\x3a\x23\x66\x66\x30\x30\x30\x30\x3b\x73\x74\x6f\ +\x70\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x3b\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x6f\x66\x66\x73\x65\x74\x3d\x22\x31\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\ +\x74\x6f\x70\x33\x31\x34\x30\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\ +\x3c\x2f\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\ +\x3e\x0a\x20\x20\x20\x20\x3c\x72\x61\x64\x69\x61\x6c\x47\x72\x61\ +\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\x22\x61\ +\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x6c\ +\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\x65\x61\ +\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x33\x36\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x61\x64\x69\x61\x6c\ +\x47\x72\x61\x64\x69\x65\x6e\x74\x33\x31\x34\x32\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x63\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\ +\x32\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x63\x79\x3d\x22\x33\ +\x34\x2e\x31\x30\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x66\x78\x3d\x22\x33\x32\x37\x2e\x34\x31\x39\x32\x35\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x66\x79\x3d\x22\x33\x34\x2e\x31\x30\ +\x31\x39\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x72\x3d\x22\ +\x31\x33\x30\x2e\x31\x33\x32\x34\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\x3d\x22\ +\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\ +\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ +\x28\x32\x2e\x35\x36\x39\x35\x37\x30\x37\x65\x2d\x37\x2c\x2d\x33\ +\x2e\x32\x34\x36\x39\x38\x35\x38\x2c\x32\x2e\x32\x39\x36\x32\x34\ +\x35\x2c\x36\x2e\x38\x37\x30\x38\x39\x36\x39\x65\x2d\x36\x2c\x33\ +\x36\x36\x2e\x39\x31\x30\x34\x39\x2c\x31\x35\x30\x34\x2e\x31\x38\ +\x33\x29\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\ +\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\ +\x63\x74\x3d\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\ +\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\ +\x31\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\ +\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\ +\x33\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\x22\x32\x36\ +\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\x2e\x36\x35\ +\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x32\x3d\x22\ +\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\x6e\x69\x74\x73\x3d\ +\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\x22\ +\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x65\x61\x72\x47\ +\x72\x61\x64\x69\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6c\x6c\x65\x63\x74\x3d\ +\x22\x61\x6c\x77\x61\x79\x73\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x78\x6c\x69\x6e\x6b\x3a\x68\x72\x65\x66\x3d\x22\x23\x6c\x69\x6e\ +\x65\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x31\x37\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\ +\x61\x72\x47\x72\x61\x64\x69\x65\x6e\x74\x34\x31\x32\x39\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x55\ +\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\ +\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x31\x3d\ +\x22\x32\x36\x30\x2e\x39\x35\x32\x34\x32\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x79\x31\x3d\x22\x31\x32\x31\x2e\x31\x38\x38\x34\x32\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x32\x3d\x22\x34\x38\x38\ +\x2e\x36\x35\x39\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\ +\x32\x3d\x22\x33\x35\x31\x2e\x34\x38\x30\x36\x35\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x67\x72\x61\x64\x69\x65\x6e\x74\x54\x72\x61\ +\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\x31\ +\x2e\x32\x34\x35\x37\x32\x32\x36\x2c\x30\x2c\x30\x2c\x31\x2e\x32\ +\x36\x31\x35\x38\x35\x34\x2c\x2d\x31\x37\x36\x2e\x37\x37\x36\x38\ +\x2c\x2d\x39\x30\x2e\x34\x39\x36\x35\x30\x38\x29\x22\x20\x2f\x3e\ +\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\x73\x6f\ +\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\ +\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x62\x61\x73\x65\x22\x0a\ +\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\x3d\x22\ +\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\ +\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\x36\x36\ +\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x6f\ +\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x2e\x30\x22\x0a\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x6f\ +\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x2e\x30\x22\x0a\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x73\ +\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\x22\x30\x2e\ +\x39\x36\x31\x36\x34\x36\x31\x31\x22\x0a\x20\x20\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x33\x32\x36\x2e\ +\x33\x38\x33\x38\x32\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x38\x39\x2e\x38\x36\x32\x38\ +\x36\x39\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\ +\x65\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2d\x75\x6e\x69\x74\x73\ +\x3d\x22\x70\x78\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\x65\ +\x72\x3d\x22\x6c\x61\x79\x65\x72\x31\x22\x0a\x20\x20\x20\x20\x20\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ +\x77\x69\x64\x74\x68\x3d\x22\x39\x39\x36\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ +\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x37\x31\x34\x22\x0a\x20\x20\ +\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\ +\x6f\x77\x2d\x78\x3d\x22\x31\x31\x22\x0a\x20\x20\x20\x20\x20\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x79\ +\x3d\x22\x31\x33\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\ +\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\ +\x74\x61\x64\x61\x74\x61\x37\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\ +\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\ +\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\ +\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\ +\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\ +\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\ +\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\ +\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\x6b\x3e\ +\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\ +\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\x20\x20\ +\x3c\x67\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\ +\x3a\x6c\x61\x62\x65\x6c\x3d\x22\x43\x61\x6c\x71\x75\x65\x20\x31\ +\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\ +\x67\x72\x6f\x75\x70\x6d\x6f\x64\x65\x3d\x22\x6c\x61\x79\x65\x72\ +\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6c\x61\x79\x65\x72\ +\x31\x22\x0a\x20\x20\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\ +\x6d\x3d\x22\x74\x72\x61\x6e\x73\x6c\x61\x74\x65\x28\x2d\x31\x37\ +\x35\x2e\x39\x35\x38\x35\x32\x2c\x2d\x31\x37\x31\x2e\x37\x39\x38\ +\x39\x39\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x66\x66\ +\x30\x30\x30\x30\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\ +\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\ +\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\ +\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ +\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ +\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ +\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\ +\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ +\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\ +\x63\x74\x32\x31\x36\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\ +\x69\x64\x74\x68\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ +\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x78\x3d\x22\x31\x38\x35\x2e\x30\x39\x39\x32\x37\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\ +\x39\x37\x34\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\ +\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x37\x36\x2e\ +\x33\x38\x37\x34\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\ +\x22\x31\x38\x30\x2e\x35\x34\x36\x39\x37\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x36\x39\x2e\x30\ +\x37\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x32\x36\x39\x2e\x30\x37\x35\x35\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x32\x31\x36\x32\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x6e\ +\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\ +\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\ +\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\x66\x30\ +\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\ +\x3a\x39\x2e\x31\x37\x36\x38\x39\x38\x39\x36\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\ +\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\ +\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\ +\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\ +\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x20\ +\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\ +\x75\x72\x6c\x28\x23\x6c\x69\x6e\x65\x61\x72\x47\x72\x61\x64\x69\ +\x65\x6e\x74\x34\x31\x32\x39\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\ +\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\ +\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\ +\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\ +\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\ +\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\ +\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x31\ +\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\ +\x35\x20\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x33\x35\ +\x32\x2e\x35\x36\x32\x35\x20\x43\x20\x31\x39\x30\x2e\x39\x33\x37\ +\x35\x32\x2c\x33\x35\x32\x2e\x37\x39\x39\x38\x32\x20\x31\x39\x36\ +\x2e\x38\x33\x38\x35\x36\x2c\x33\x35\x32\x2e\x39\x33\x37\x35\x20\ +\x32\x30\x32\x2e\x37\x38\x31\x32\x35\x2c\x33\x35\x32\x2e\x39\x33\ +\x37\x35\x20\x43\x20\x33\x31\x32\x2e\x38\x30\x30\x31\x34\x2c\x33\ +\x35\x32\x2e\x39\x33\x37\x35\x20\x34\x30\x36\x2e\x39\x35\x30\x34\ +\x39\x2c\x33\x31\x31\x2e\x36\x31\x30\x34\x34\x20\x34\x34\x35\x2e\ +\x30\x36\x32\x35\x2c\x32\x35\x33\x2e\x32\x35\x20\x4c\x20\x34\x34\ +\x35\x2e\x30\x36\x32\x35\x2c\x31\x38\x30\x2e\x39\x33\x37\x35\x20\ +\x4c\x20\x31\x38\x35\x2e\x30\x39\x33\x37\x35\x2c\x31\x38\x30\x2e\ +\x39\x33\x37\x35\x20\x7a\x20\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x34\x22\x20\x2f\x3e\ +\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x74\x79\x70\x65\x3d\ +\x22\x61\x72\x63\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\ +\x6c\x65\x3d\x22\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\ +\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\ +\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\ +\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\ +\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\ +\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\ +\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\ +\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\ +\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\ +\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\ +\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x31\x34\x36\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x63\x78\x3d\x22\x33\x30\x33\x2e\x36\x34\x36\x22\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x63\x79\x3d\ +\x22\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x72\x78\x3d\x22\ +\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6f\x64\x69\x70\x6f\ +\x64\x69\x3a\x72\x79\x3d\x22\x37\x2e\x32\x37\x39\x31\x38\x35\x33\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x33\x30\ +\x33\x2e\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\ +\x41\x20\x30\x20\x37\x2e\x32\x37\x39\x31\x38\x35\x33\x20\x30\x20\ +\x31\x20\x31\x20\x20\x33\x30\x33\x2e\x36\x34\x36\x2c\x31\x32\x39\ +\x2e\x39\x38\x35\x34\x34\x20\x41\x20\x30\x20\x37\x2e\x32\x37\x39\ +\x31\x38\x35\x33\x20\x30\x20\x31\x20\x31\x20\x20\x33\x30\x33\x2e\ +\x36\x34\x36\x20\x31\x32\x39\x2e\x39\x38\x35\x34\x34\x20\x7a\x22\ +\x20\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x72\x65\x63\x74\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x79\x3d\x22\x31\x38\x30\x2e\x39\x33\x39\x37\ +\x34\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x31\x38\x35\ +\x2e\x30\x39\x39\x32\x37\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\ +\x65\x69\x67\x68\x74\x3d\x22\x32\x35\x39\x2e\x39\x37\x30\x38\x39\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\ +\x32\x35\x39\x2e\x39\x37\x30\x38\x39\x22\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x33\x31\x33\x34\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x6f\x70\ +\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x75\x72\x6c\ +\x28\x23\x72\x61\x64\x69\x61\x6c\x47\x72\x61\x64\x69\x65\x6e\x74\ +\x33\x31\x34\x32\x29\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\ +\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\ +\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x66\ +\x66\x66\x66\x66\x66\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\ +\x74\x68\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\ +\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\ +\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\ +\x68\x61\x72\x72\x61\x79\x3a\x30\x2e\x32\x39\x33\x39\x30\x36\x36\ +\x36\x2c\x20\x30\x2e\x32\x39\x33\x39\x30\x36\x36\x36\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\ +\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\ +\x3a\x31\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\ +\x73\x76\x67\x3e\x0a\ +" + +qt_resource_name = "\ +\x00\x05\ +\x00\x6f\xa6\x53\ +\x00\x69\ +\x00\x63\x00\x6f\x00\x6e\x00\x73\ +\x00\x0f\ +\x01\x19\xe4\x67\ +\x00\x6c\ +\x00\x65\x00\x64\x00\x5f\x00\x72\x00\x65\x00\x64\x00\x5f\x00\x6f\x00\x66\x00\x66\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0d\ +\x0e\xf5\xe6\x07\ +\x00\x6c\ +\x00\x65\x00\x64\x00\x5f\x00\x67\x00\x72\x00\x65\x00\x65\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x11\ +\x09\x14\xa8\xe7\ +\x00\x6c\ +\x00\x65\x00\x64\x00\x5f\x00\x67\x00\x72\x00\x65\x00\x65\x00\x6e\x00\x5f\x00\x6f\x00\x66\x00\x66\x00\x2e\x00\x73\x00\x76\x00\x67\ +\ +\x00\x0b\ +\x08\x52\x2e\x07\ +\x00\x6c\ +\x00\x65\x00\x64\x00\x5f\x00\x72\x00\x65\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +" + +qt_resource_struct = "\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\ +\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x39\xd9\ +\x00\x00\x00\x54\x00\x00\x00\x00\x00\x01\x00\x00\x29\x35\ +\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x14\x9b\ +" + +def qInitResources(): + QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/hpgl_qt.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +import numpy +import pylab +import math +import new # monkey patching... + +from PyQt4 import QtGui, QtCore +from PyQt4.QtCore import Qt, SIGNAL + +from hpgl_parser import HPGLParser + +class QHPGLPlotterWidget(HPGLParser, QtGui.QWidget): + pen_styles = {2: [0.4, 0.6], + 3: [0.6, 0.4], + 4: [0.7, 0.1, 0.1, 0.1], + 5: [0.6, 0.1, 0.2, 0.1], + 6: [0.45, 0.1, 0.1, 0.2, 0.1, 0.05], + } + pen_colors = ["black", "green", "red", "blue", "yellow", "cyan", "magenta", + "darkred", "darkblue"] + + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + l = QtGui.QVBoxLayout(self) + l.setMargin(1) + self.qview = QtGui.QGraphicsView(self) + self.qview.setRenderHints(QtGui.QPainter.Antialiasing) + self.qview.scale(0.5,-0.5) + l = self.layout() + l.addWidget(self.qview) + self.setBackgroundRole(QtGui.QPalette.Base) + self.setSizePolicy(QtGui.QSizePolicy.Expanding, + QtGui.QSizePolicy.Expanding) + self.clear() + HPGLParser.__init__(self) + + def parse(self, data): + HPGLParser.parse(self, data) + self.resize(self.size()) + + def clear(self): + self.qpen = QtGui.QPen(QtCore.Qt.blue) + self.qbrush = QtGui.QBrush(QtCore.Qt.blue) + self.qfont = QtGui.QFont('Courier') + self.qantialiased = False + self.qtransformed = False + self.qscene = QtGui.QGraphicsScene() + self.qview.setScene(self.qscene) + + def _get_PW(self): + return self._pen_width + def _set_PW(self, value): + value = float(value) + #print "set pen width", value, value*300.0/75.0 + #self.qpen.setWidthF(value*300.0/75.0) + self._pen_width = value + pen_width = property(_get_PW, _set_PW) + + def LT(self): + HPGLParser.LT(self) + if self.line_type == 0: + self.qpen.setStyle(Qt.SolidLine) + elif self.line_type == 1: + self.qpen.setStyle(Qt.DotLine) + else: + pattern = numpy.array(self.pen_styles[self.line_type]) + x0, x1, y0, y1 = self.scale + dist = math.sqrt((x1-x0)**2 + (y1-y0)**2) + pattern = self.pattern_len*dist/100.0 * pattern + if self.qpen.widthF()>0: + pattern = pattern/self.qpen.widthF() + self.qpen.setDashPattern(pattern.tolist()) + + def SP(self): + HPGLParser.SP(self) + color = self.pen_colors[self.pen] + if isinstance(color, tuple): + color = QtGui.QColor(*color) + else: + color = QtGui.QColor(color) + self.qpen.setColor(color) + + def parse(self, data): + HPGLParser.parse(self, data) + self.update() + self.qview.fitInView(self.qscene.sceneRect(), Qt.KeepAspectRatio) + + def minimumSizeHint(self): + return QtCore.QSize(100, 100) + + def sizeHint(self): + return QtCore.QSize(400, 400) + + def resizeEvent(self, event): + self.qview.fitInView(self.qscene.sceneRect(), Qt.KeepAspectRatio) + QtGui.QWidget.resizeEvent(self, event) + + def plot_lines(self, points): + if len(points) == 1: + return + pa = QtGui.QPainterPath() + pa.addPolygon(QtGui.QPolygonF([QtCore.QPointF(*p) for p in points])) + self.qscene.addPath(pa, self.qpen) + + def plot_symbols(self, points): + pass + + def plot_string(self, s): + cw, ch = self.get_char_size() + # this is very ugly and so, but I don't understand haw is + # computed string positionning in HPGL... + t0 = self.qscene.addSimpleText(" ", self.qfont) + br = t0.boundingRect() + mw = br.width() + mh = br.height() + self.qscene.removeItem(t0) + # don't ask me why theses constants are here, they seem OK, that's all + t = self.qscene.addSimpleText(s, self.qfont) + t.scale(1.0 * cw/mw, -1.6 * ch/mh) + x, y = self.pos + t.moveBy(x,y) + br = t.boundingRect() + t.moveBy(-0.5*cw+8, math.sqrt(2)*ch - 13) + + +if __name__=='__main__': + import sys + a = QtGui.QApplication(sys.argv) + mw = QtGui.QMainWindow() + w = QtGui.QWidget(mw) + mw.setCentralWidget(w) + + l = QtGui.QVBoxLayout(w) + p = QHPGLPlotterWidget(w) + l.addWidget(p, 1) + b = QtGui.QPushButton("Replot", w) + l.addWidget(b) + def plot(): + p.parse(open(sys.argv[1]).read()) + b.connect(b, SIGNAL('pressed()'), plot) + mw.show() + plot() + a.exec_() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/qgpib_plotter.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,346 @@ +# + +import os, sys +import time +import glob + +from PyQt4 import QtGui, QtCore, uic +from PyQt4.QtCore import SIGNAL, Qt + +if "-m" in sys.argv: + from gpib_plotter_mockup import GPIBplotter +else: + from gpib_plotter import GPIBplotter +from hpgl_qt import QHPGLPlotterWidget + +ldir = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(ldir) +form_class, base_class = uic.loadUiType(os.path.join(ldir, "qhpgl_plotter.ui")) + +from pygpibtoolkit.qt4.qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem +from pygpibtoolkit.qt4.qpreferences import PointItem, SizeItem, ByteArrayItem +from pygpibtoolkit.qt4.qpreferences import AbstractPreferences +from pygpibtoolkit.qt4.qpreferenceseditor import PreferencesEditor + +from pygpibtoolkit.tools import str_num_cmp + +class Preferences(AbstractPreferences): + ORGANISATION="Logilab" + APPLICATION="qgpib_plotter" + + _pos = PointItem() + _size = SizeItem() + _appState = ByteArrayItem() + + device = UnicodeItem(default='/dev/ttyUSB0', + name=u'device', + description=u'GPIB device', + group="GPIB settings") + address = IntItem(default=5, min=0, max=16, + name=u'GPIB address', + group="GPIB settings") + + background = ColorItem(default=QtGui.QColor("white"), + name="Background", + group="Colors") + color0 = ColorItem(default=QtGui.QColor("black"), + name="Pen #0", + group="Colors") + color1 = ColorItem(default=QtGui.QColor("green"), + name="Pen #1", + group="Colors") + color2 = ColorItem(default=QtGui.QColor("red"), + name="Pen #2", + group="Colors") + color3 = ColorItem(default=QtGui.QColor("blue"), + name="Pen #3", + group="Colors") + color4 = ColorItem(default=QtGui.QColor("yellow"), + name="Pen #4", + group="Colors") + color5 = ColorItem(default=QtGui.QColor("cyan"), + name="Pen #5", + group="Colors") + color6 = ColorItem(default=QtGui.QColor("magenta"), + name="Pen #6", + group="Colors") + color7 = ColorItem(default=QtGui.QColor("darkred"), + name="Pen #7", + group="Colors") + +class QtHPGLPlotter(QtGui.QMainWindow, form_class): + def __init__(self, parent=None): + QtGui.QMainWindow.__init__(self, parent) + self._plots = {} + self._prefs = Preferences() + self.setupUi() + self.initializeGPIB() + if self._prefs._pos: + self.move(self._prefs._pos) + if self._prefs._size: + self.resize(self._prefs._size) + if self._prefs._appState: + self.restoreState(self._prefs._appState) + self.readPreferences() + + def readPreferences(self): + bg = self._prefs.background + if bg and bg.isValid(): + self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg)) + pen_colors = [self._prefs["color%d"%i] for i in range(8)] + self.plotterWidget.pen_colors = pen_colors + + def replotCurrent(self): + self.currentPlotChanged(self.plotsView.currentIndex()) + + def setupUi(self): + form_class.setupUi(self, self) # call qtdesigner generated form creation + # actions defined in designer + self.connect(self.actionPreferences, SIGNAL('triggered(bool)'), + self.preferencesTriggered) + self.connect(self.actionQuit, SIGNAL('triggered(bool)'), + self.quitTriggered) + self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q')) + self.connect(self.actionOpen, SIGNAL('triggered(bool)'), + self.openTriggered) + self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O')) + self.connect(self.actionSave, SIGNAL('triggered(bool)'), + self.saveTriggered) + self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S')) + self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'), + self.saveAsTriggered) + + self.plotterWidget = QHPGLPlotterWidget(self) + self.setCentralWidget(self.plotterWidget) + + self.connect(self.captureButton, SIGNAL("toggled(bool)"), + self.captureToggled) + + self._plots_list = QtGui.QStringListModel() + self.plotsView.setModel(self._plots_list) + self.connect(self.plotsView, SIGNAL('activated(const QModelIndex&)'), + self.currentPlotChanged) + self.connect(self.plotsView.selectionModel(), + SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)'), + self.currentPlotChanged) + + def currentPlotChanged(self, index, old_index=None): + if index.isValid(): + value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) + + self.plotterWidget.clear() + self.plotterWidget.parse(self._plots[value]) + + def preferencesTriggered(self, checked=False): + PreferencesEditor(self._prefs, self).exec_() + self.readPreferences() + self.replotCurrent() + + def quitTriggered(self, checked=False): + self.close() + + def closeEvent(self, event): + if 1: + #if self.promptForSave(): + self._prefs._pos = self.pos() + self._prefs._size = self.size() + self._prefs._appState = self.saveState() + event.accept() + else: + event.ignore() + + def openTriggered(self, checked=False): + filenames = QtGui.QFileDialog.getOpenFileNames(self, "Open a HPGL file to display", '.', 'HPGL files (*.plt)\nAll files (*)') + self.openFiles(filenames) + self.displayFirst() + + def displayFirst(self): + if not self.plotsView.currentIndex().isValid(): + self.plotsView.setCurrentIndex(self.plotsView.model().index(0, 0)) + + def openFiles(self, filenames): + ok = False + for filename in filenames: + filename = str(filename) + if os.path.exists(filename): + data = open(filename).read() + name = os.path.basename(filename) + name = os.path.splitext(name)[0] + lst = self.plotsView.model().stringList() + lst.append(name) + self._plots[name] = data + self.plotsView.model().setStringList(lst) + ok = True + return ok + + def plotReceived(self, num): + self._receiving = False + self.setReceivingLed() + plot, timestamp = self.captureThread.getPlot(num) + name = "plot_%s"%(num) + lst = self.plotsView.model().stringList() + lst.append(name) + self._plots[name] = plot + self.plotsView.model().setStringList(lst) + + def plotStarted(self): + self._receiving = True + self.setReceivingLed() + + def saveTriggered(self, checked=False): + print "save" + + def saveAsTriggered(self, checked=False): + index = self.plotsView.selectionModel().currentIndex() + if index.isValid(): + filename = QtGui.QFileDialog.getSaveFileName(self, "Selecte a file name to save HPGL file", '.', 'HPGL files (*.plt)\nAll files (*)') + n = index.row() + value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString()) + open(filename, 'w').write(self._plots[value]) + + + def initializeGPIB(self): + self._online = False + try: + self.gpib_plotter = QGPIBplotter(device=self._prefs.device, + address=self._prefs.address, + ) + self.captureThread = GPIBReceiver(self.gpib_plotter) + self.connect(self.captureThread, SIGNAL('plotReceived(int)'), + self.plotReceived) + self.connect(self.captureThread, SIGNAL('plotStarted()'), + self.plotStarted) + self.captureThread.start() + except Exception, e: + #print e + self.gpib_plotter = None + self.setCaptureLed() + + def captureToggled(self, state): + if state: + if self.gpib_plotter is None: + self.initializeGPIB() + if self.gpib_plotter is None: + QtGui.QMessageBox.critical(self, self.tr("GPIB error"), + self.tr("<b>Unable to initialize GPIB connection</b>.<br>Please check your GPIB dongle and settings.")) + self._online = False + self.setCaptureLed() + return + self._online = True + self.captureThread.startCapture() + else: + if self.captureThread: + self.captureThread.stopCapture() + self._online = False + self.setCaptureLed() + + def setCaptureLed(self): + if self._online: + icn = QtGui.QIcon(':/icons/led_green.svg') + else: + icn = QtGui.QIcon(':/icons/led_green_off.svg') + self.captureButton.setIcon(icn) + self.captureButton.setChecked(self._online) + + def setReceivingLed(self): + if self._receiving: + icn = QtGui.QIcon(':/icons/led_red.svg') + else: + icn = QtGui.QIcon(':/icons/led_red_off.svg') + self.receivingButton.setIcon(icn) + +class QGPIBplotter(GPIBplotter): + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=5): + GPIBplotter.__init__(self, device, baudrate, timeout, address) + self.emitter = None + + def plotStarted(self): + if self.emitter: + self.emitter.emit(SIGNAL('plotStarted()')) + #self.emitter.msleep(1) + +class GPIBReceiver(QtCore.QThread): + def __init__(self, cnx): + QtCore.QThread.__init__(self) + self.gpibplotter = cnx + self.gpibplotter.emitter = self + + self._cancelmutex = QtCore.QMutex() + self._cancel = False + #self._nreceived = 0 + self._plotsmutex = QtCore.QMutex() + self._plots = [] + self._startstopmutex = QtCore.QMutex() + self._startstop = QtCore.QWaitCondition() + self._capturing = False + + def cancel(self): + self._cancelmutex.lock() + self._cancel = True + self._cancelmutex.unlock() + + def startCapture(self): + self._startstop.wakeOne() + + def stopCapture(self): + self._startstopmutex.lock() + self._capturing = False + self._startstopmutex.unlock() + + def run(self): + while 1: + self._cancelmutex.lock() + if self._cancel: + return + self._cancelmutex.unlock() + self._startstopmutex.lock() + if not self._capturing: + self._startstop.wait(self._startstopmutex) + self._capturing = True + self._startstopmutex.unlock() + + plot = self.gpibplotter.load_plot(wait_timeout=0.1) + timestamp = time.time() + if plot: + self._plotsmutex.lock() + self._plots.append((plot, timestamp)) + n = len(self._plots) + self._plotsmutex.unlock() + self.emit(SIGNAL('plotReceived(int)'), n-1) + self.msleep(10) + + def getPlot(self, num): + self._plotsmutex.lock() + try: + return self._plots[num] + finally: + self._plotsmutex.unlock() + +def main(): + import optparse + opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') + opt.add_option('-m', '--mockup', default=False, + action="store_true", + dest='mockup', + help='Use a pseudo GPIB connection (for test purpose)', + ) + opt.add_option('-v', '--verbose', default=False, + action="store_true", + dest="verbose", + help="Verbose mode",) + + options, argv = opt.parse_args(sys.argv) + + a = QtGui.QApplication(argv) + w = QtHPGLPlotter() + files = [f for f in argv[1:] if os.path.isfile(f)] + files.sort(cmp=str_num_cmp) + if w.openFiles(files): + w.displayFirst() + + w.show() + a.exec_() + +if __name__ == '__main__': + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/plotter/qhpgl_plotter.ui Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,166 @@ +<ui version="4.0" > + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>507</width> + <height>492</height> + </rect> + </property> + <property name="windowTitle" > + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget" /> + <widget class="QMenuBar" name="menubar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>507</width> + <height>25</height> + </rect> + </property> + <widget class="QMenu" name="menu_File" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="actionOpen" /> + <addaction name="actionSave" /> + <addaction name="actionSaveAs" /> + <addaction name="separator" /> + <addaction name="actionQuit" /> + </widget> + <widget class="QMenu" name="menuEdit" > + <property name="title" > + <string>Edit</string> + </property> + <addaction name="actionPreferences" /> + </widget> + <addaction name="menu_File" /> + <addaction name="menuEdit" /> + </widget> + <widget class="QStatusBar" name="statusbar" /> + <widget class="QDockWidget" name="plotsDock" > + <attribute name="dockWidgetArea" > + <number>1</number> + </attribute> + <widget class="QWidget" name="dockWidgetContents" > + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>4</number> + </property> + <property name="leftMargin" > + <number>1</number> + </property> + <property name="topMargin" > + <number>4</number> + </property> + <property name="rightMargin" > + <number>1</number> + </property> + <property name="bottomMargin" > + <number>1</number> + </property> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QToolButton" name="captureButton" > + <property name="minimumSize" > + <size> + <width>0</width> + <height>29</height> + </size> + </property> + <property name="text" > + <string>on line</string> + </property> + <property name="icon" > + <iconset/> + </property> + <property name="iconSize" > + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="receivingButton" > + <property name="text" > + <string/> + </property> + <property name="icon" > + <iconset/> + </property> + <property name="iconSize" > + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="flat" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QListView" name="plotsView" /> + </item> + </layout> + </widget> + </widget> + <action name="actionOpen" > + <property name="text" > + <string>&Open</string> + </property> + </action> + <action name="actionSave" > + <property name="text" > + <string>&Save</string> + </property> + </action> + <action name="actionSaveAs" > + <property name="text" > + <string>Save as...</string> + </property> + </action> + <action name="actionQuit" > + <property name="text" > + <string>&Quit</string> + </property> + </action> + <action name="actionPreferences" > + <property name="text" > + <string>Preferences</string> + </property> + </action> + </widget> + <resources> + <include location="../qt4/resources.qrc" /> + </resources> + <connections/> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/prologix.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,117 @@ +""" +prologix +======== + +Module defining a communication object to talk to Prologix USB-GPIB controler. + +""" +import serial +import time + +from pygpibtoolkit import pygpib +from pygpibtoolkit.pygpib import ConnectionError + +GPIB_CONTROLLER = 1 +GPIB_DEVICE = 0 + +class GPIB(object): + _retries = 15 + def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1, + address=0, mode=1): + """ + Create a new GPIB controller for the Prologix USB-GPIB device + located on serial device 'device'. + """ + self._cnx = serial.Serial(port=device, baudrate=baudrate, timeout=timeout) + self._timeout = timeout + + self.set_mode(mode) + self.set_address(address) + + def set_address(self, address): + """ + Set the address of the GPIB device: + + - if the device is the Controller In Charge, this is the + address of the device commands are sent to, + + - if the device is in GPIB_DEVICE mode, this is its address. + """ + self._set_cmd('addr', address) + self._adress = address + + def set_mode(self, mode): + """ + Set GPIB device mode to 'mode': + + - GPIB_CONTROLLER: set the device as the Controller In Charge + on the GPIB bus + + - GPIB_DEVICE: set the device as a standard GPIB device on the + bus. + """ + self._set_cmd('mode', mode) + self._mode = mode + + def set_controller(self): + """ + Set GPIB device the Controller In Charge on the GPIB bus. + """ + self.set_mode(1) + + def set_device(self): + """ + Set the GPIB device as a simple device on the GPIB bus. + """ + self.set_mode(0) + + def send_command(self, cmd): + """ + Send the specified GPIB command on the bus (must be the CIC), + and read the answer. + """ + assert self._mode == 1 + self._cnx.write(cmd+'\r') + time.sleep(self._timeout) # required? + ret = self._cnx.readlines() + return ''.join(ret) + + def check_srq(self): + """ + Check the SRQ line + """ + assert self._mode == 1, "must be the Controller In Charge" + self._cnx.write('++srq\r') + ret = self._cnx.readline().strip() + if ret: + return bool(int(ret)) + return None + + def poll(self): + """ + Poll every address, and return a dictionnary + {add: status, ...} + """ + assert self._mode == 1, "must be the Controller In Charge" + dico = {} + for add in range(31): + self._cnx.write('++spoll %d\r'%i) + ret = self._cnx.readline().strip() + if ret: + dico[i] = int(ret) + return dico + + def _read(self): + for i in range(self._retries): + rdata = self._cnx.readline() + if rdata.strip() != "": + break + time.sleep(self._timeout) + return rdata + + def _set_cmd(self, cmd, value): + self._cnx.write('++%s %d\r'%(cmd, value)) + self._cnx.write('++%s\r'%(cmd)) + rval = self._read().strip() + if not rval.isdigit() or int(rval) != value: + raise ConnectionError("Can't set GPIB %s to %s [ret=%s]"%(cmd, value, repr(rval)))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/pygpib.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,64 @@ +""" +gpib: create serial connection to GPIB-USB device (ProLogix is the +only supported device for now). +""" +import serial +from serial.serialutil import SerialException +import time + +class ConnectionError(Exception): + pass + +class Constants(object): + def __init__(self): + self.constants = {} + self.descriptions = {} + self.rev_constants = {} + for v, k, m in self._constants: + self.k = v + self.constants[v] = k + self.rev_constants[k] = v + self.descriptions[v] = m + + def __getitem__(self, k): + if isinstance(k, basestring): + return self.rev_constants[k] + else: + return self.constants[k] + + def get_description(self, k): + if isinstance(k, basestring): + k = self.rev_constants[k] + return self.descriptions[k] + + +class MODE(Constants): + _constants = [(1, "CONTROLLER", "Set device as Controller in Charge"), + (0, "DEVICE", "Set device as simple listener"), + ] +# TODO +# class STATUS_BYTE(Constants): +# # IEEE 488.2 Status Byte constants +# MAV = 0x10 # Message AVailable: bit 4 of the Status Byte +# ESB = 0x20 # Event Status Bit: bit 5 of the Status Byte +# MSS = 0x40 # Master Summary Status bit: bit 6 of the Status Byte (NOT +# # sent in response to a serial poll) +# RQS = 0x40 # Request Service: bit 6 of the Status Byte (when sent in +# # response to a serial poll) +# class SESR(Constants): +# # SESR constants (Standard Event Status Register) +# PON = 0x80 # Power On: Power has been turned On since last register +# # read access +# URQ = 0x40 # User Request: the user has activated some device control +# # (whatever the Remote Local state is) +# CME = 0x20 # Command Error +# EXE = 0x10 # Execution Error +# DDE = 0x08 # Device Dependant Error +# QYE = 0x04 # QuerY Error (attempt to read data while Output Queue is +# # empty, or data in the OQ was lost) +# RQC = 0x02 # Request Control: tell the CiC that the device wants to +# # become the CiC +# OPC = 0x01 # Operation Complete: device has completed any pending +# # operation (ready to accept new commands). This bit is +# # generated in response to a OPC command. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/icons/led_green.svg Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="278.25241" + height="278.25241" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docbase="/home/david/Electronic/HP3562/icons" + sodipodi:docname="led_green.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient4117"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4119" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4121" /> + </linearGradient> + <linearGradient + id="linearGradient3136"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3138" /> + <stop + style="stop-color:#00ff00;stop-opacity:0;" + offset="1" + id="stop3140" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3136" + id="radialGradient3142" + cx="327.41925" + cy="34.101982" + fx="327.41925" + fy="34.101982" + r="130.1324" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.5695707e-7,-3.2469858,2.296245,6.8708969e-6,366.91049,1504.183)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4123" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4129" + gradientUnits="userSpaceOnUse" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.48082306" + inkscape:cx="372.04724" + inkscape:cy="207.1213" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:window-width="996" + inkscape:window-height="714" + inkscape:window-x="25" + inkscape:window-y="4" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-175.95852,-171.79899)"> + <rect + style="opacity:1;fill:#00ff00;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" + id="rect2160" + width="259.97089" + height="259.97089" + x="185.09927" + y="180.93974" /> + <rect + y="176.38744" + x="180.54697" + height="269.0755" + width="269.0755" + id="rect2162" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#00c40b;stroke-width:9.17689896;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + y="180.93974" + x="185.09927" + height="259.97089" + width="259.97089" + id="rect3134" + style="opacity:1;fill:url(#radialGradient3142);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" + d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " + id="path3144" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path3146" + sodipodi:cx="303.646" + sodipodi:cy="129.98544" + sodipodi:rx="0" + sodipodi:ry="7.2791853" + d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/icons/led_green_off.svg Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="278.25241" + height="278.25241" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docbase="/home/david/Electronic/HP3562/icons" + sodipodi:docname="led_green_off.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0"> + <defs + id="defs4"> + <linearGradient + id="linearGradient4117"> + <stop + style="stop-color:#acacac;stop-opacity:0.68783069;" + offset="0" + id="stop4119" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4121" /> + </linearGradient> + <linearGradient + id="linearGradient3136"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3138" /> + <stop + style="stop-color:#00ff00;stop-opacity:0;" + offset="1" + id="stop3140" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4129" + gradientUnits="userSpaceOnUse" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.48082306" + inkscape:cx="372.04724" + inkscape:cy="-64.793696" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:window-width="996" + inkscape:window-height="714" + inkscape:window-x="22" + inkscape:window-y="21" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-175.95852,-171.79899)"> + <rect + style="opacity:1;fill:#009900;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" + id="rect2160" + width="259.97089" + height="259.97089" + x="185.09927" + y="180.93974" /> + <rect + y="176.38744" + x="180.54697" + height="269.0755" + width="269.0755" + id="rect2162" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#008304;stroke-width:9.17689896;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" + d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " + id="path3144" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path3146" + sodipodi:cx="303.646" + sodipodi:cy="129.98544" + sodipodi:rx="0" + sodipodi:ry="7.2791853" + d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/icons/led_red.svg Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="278.25241" + height="278.25241" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docbase="/home/david/Electronic/HP3562/icons" + sodipodi:docname="led_red.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient4117"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4119" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4121" /> + </linearGradient> + <linearGradient + id="linearGradient3136"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3138" /> + <stop + style="stop-color:#ff0000;stop-opacity:0;" + offset="1" + id="stop3140" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3136" + id="radialGradient3142" + cx="327.41925" + cy="34.101982" + fx="327.41925" + fy="34.101982" + r="130.1324" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.5695707e-7,-3.2469858,2.296245,6.8708969e-6,366.91049,1504.183)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4123" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4129" + gradientUnits="userSpaceOnUse" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.96164611" + inkscape:cx="326.38382" + inkscape:cy="89.862869" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:window-width="996" + inkscape:window-height="714" + inkscape:window-x="11" + inkscape:window-y="13" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-175.95852,-171.79899)"> + <rect + style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" + id="rect2160" + width="259.97089" + height="259.97089" + x="185.09927" + y="180.93974" /> + <rect + y="176.38744" + x="180.54697" + height="269.0755" + width="269.0755" + id="rect2162" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ff0000;stroke-width:9.17689896;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" + d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " + id="path3144" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path3146" + sodipodi:cx="303.646" + sodipodi:cy="129.98544" + sodipodi:rx="0" + sodipodi:ry="7.2791853" + d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> + <rect + y="180.93974" + x="185.09927" + height="259.97089" + width="259.97089" + id="rect3134" + style="opacity:1;fill:url(#radialGradient3142);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/icons/led_red_off.svg Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="278.2525" + height="278.2525" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docbase="/home/david/Electronic/HP3562/icons" + sodipodi:docname="led_red_off.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient4117"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4119" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4121" /> + </linearGradient> + <linearGradient + id="linearGradient3136"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3138" /> + <stop + style="stop-color:#aa0000;stop-opacity:0;" + offset="1" + id="stop3140" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3136" + id="radialGradient3142" + cx="327.41925" + cy="34.101982" + fx="327.41925" + fy="34.101982" + r="130.1324" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.5695707e-7,-3.2469858,2.296245,6.8708969e-6,366.91049,1504.183)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4123" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4117" + id="linearGradient4129" + gradientUnits="userSpaceOnUse" + x1="260.95242" + y1="121.18842" + x2="488.65955" + y2="351.48065" + gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.96164611" + inkscape:cx="326.38382" + inkscape:cy="69.71549" + inkscape:document-units="px" + inkscape:current-layer="layer1" + inkscape:window-width="996" + inkscape:window-height="714" + inkscape:window-x="11" + inkscape:window-y="13" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-175.95847,-171.79894)"> + <rect + y="180.93974" + x="185.09927" + height="259.97089" + width="259.97089" + id="rect3134" + style="opacity:1;fill:url(#radialGradient3142);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + style="opacity:1;fill:#a20000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" + id="rect2160" + width="259.97089" + height="259.97089" + x="185.09927" + y="180.93974" /> + <rect + y="176.38744" + x="180.54697" + height="269.0755" + width="269.0755" + id="rect2162" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#950000;stroke-width:9.17700005;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" + d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " + id="path3144" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path3146" + sodipodi:cx="303.646" + sodipodi:cy="129.98544" + sodipodi:rx="0" + sodipodi:ry="7.2791853" + d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/qpreferences.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,150 @@ +import os +from PyQt4 import QtCore, QtGui, uic +from tools import AbstractRegister + +def fromVariant(v): + _cvrts = {0: lambda x:None, + 1: lambda x:x.toBool(), + 2: lambda x:x.toInt()[0], + 6: lambda x:x.toDouble()[0], + 10: lambda x:unicode(x.toString()), + 12: lambda x:x.toByteArray(), + 21: lambda x:x.toSize(), + 22: lambda x:x.toSizeF(), + 25: lambda x:x.toPoint(), + 26: lambda x:x.toPointF(), + 64: lambda x:QtGui.QFont(x), + 67: lambda x:QtGui.QColor(x), + } + t = v.userType() + return _cvrts[t](v) + +class PreferenceMetaclass(type): + _widgets = {} + def __init__(cls, name, bases, dct): + # called at class creation + super(type, cls).__init__(name, bases, dct) + if name != "BaseItem": + ItemRegister().add(cls) + +class BaseItem(object): + #__metaclass__ = PreferenceMetaclass + _id = 0 + def __init__(self, default=None, name=None, description=None, group=None): + self._default = default + self._id = BaseItem._id + self._name = name + self._description = description + self._group = group + BaseItem._id += 1 + + def validate(self, value): + return True + + def __get__(self, obj, cls): + if obj is None: #when called from the class, return the Item itself + return self + try: + return obj.getPref(self._id) + except Exception, e: + return None + + def __set__(self, obj, value): + obj.setPref(self._id, value) + +class ItemRegister(AbstractRegister): + _registered_type = BaseItem + getItem = AbstractRegister.get_class + +class PointItem(BaseItem): + _type = QtCore.QPoint + +class SizeItem(BaseItem): + _type = QtCore.QSize + +class ByteArrayItem(BaseItem): + _type = QtCore.QByteArray + +class UnicodeItem(BaseItem): + _type = unicode + def validate(self, value): + return isinstance(value, basestring) + +class IntItem(BaseItem): + _type = int + def __init__(self, default=None, name=None, description=None, group=None, min=None, max=None): + BaseItem.__init__(self, default, name, description, group) + self._min = min + self._max = max + + def validate(self, value): + try: + value = self._type(value) + except: + return False + if self._min is not None and value<self._min: + return False + if self._max is not None and value>self._max: + return False + return True + +class ColorItem(BaseItem): + _type = QtGui.QColor + + def validate(self, value): + try: + self._type(value) + return True + except: + return False + +class AbstractPreferences(QtCore.QObject): + def __init__(self): + QtCore.QObject.__init__(self) + self._settings = QtCore.QSettings(QtCore.QSettings.UserScope, + self.ORGANISATION, self.APPLICATION) + self._prefs = {} + self.groups = [] + keys = [] + for k in dir(self.__class__): + item = self.getItem(k) + if isinstance(item, BaseItem): + keys.append((k,item)) + keys.sort(key=lambda x: x[1]._id) + for k, item in keys: + self._prefs[item._id] = k + if item._group not in self.groups: + self.groups.append(item._group) + + def getItem(self, key): + return getattr(self.__class__, key) + #return self._prefs.get(key, None) + + def getPref(self, key): + key = self._prefs.get(key, key) + default = getattr(self.__class__, key)._default + if default is not None: + default = QtCore.QVariant(default) + else: + default = QtCore.QVariant() + val = fromVariant(self._settings.value(key, default)) + return val + + def setPref(self, key, value): + key = self._prefs.get(key, key) + self._settings.setValue(key, QtCore.QVariant(value)) + + def keys(self, group=None): + return [k for k in self._prefs.values() if not k.startswith('_') and self.getItem(k)._group == group] + + def getName(self, key): + item = getattr(self.__class__, key) + return item._name + + def getDescription(self, key): + item = getattr(self.__class__, key) + return item._description + + def __getitem__(self, key): + return getattr(self, key) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/qpreferences_dialog.ui Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,75 @@ +<ui version="4.0" > + <class>PreferencesDialog</class> + <widget class="QDialog" name="PreferencesDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QTabWidget" name="centralTab" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="tab" > + <attribute name="title" > + <string>Tab 1</string> + </attribute> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>PreferencesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>PreferencesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/qpreferenceseditor.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,231 @@ +import os +from PyQt4 import QtCore, QtGui, uic +from PyQt4.QtCore import Qt, SIGNAL +import sip +from qpreferences import fromVariant +from qpreferences import UnicodeItem +from tools import AbstractRegister + +class WidgetMetaclass(sip.wrappertype): + _widgets = {} + def __init__(cls, name, bases, dct): + # called at class creation + super(WidgetMetaclass, cls).__init__(name, bases, dct) + if name != "BaseWidget": + WidgetRegister().add(cls) + +class BaseWidget(QtGui.QWidget): + __metaclass__ = WidgetMetaclass + _filter = None + +class WidgetRegister(AbstractRegister): + _registered_type = BaseWidget + getWidget = AbstractRegister.get_class + +form_class, base_class = uic.loadUiType(os.path.join(os.path.dirname(__file__), "qpreferences_dialog.ui")) + +class ItemValidator(QtGui.QValidator): + def __init__(self, parent, item): + QtGui.QValidator.__init__(self, parent) + self._item = item + + def validate(self, value, pos): + value = unicode(value) + if value.strip() == "": + return (self.Intermediate, pos) + if self._item.validate(value): + return (self.Acceptable, pos) + return (self.Invalid, pos) + +class BaseEditor(BaseWidget): + """ + Basic editor for preference items. Use a QLineEdit with no + validation or so... + """ + _accepts = "UnicodeItem" + def __init__(self, parent, item): + BaseWidget.__init__(self, parent) + self._item = item + self.setupUI() + + def setValue(self, value): + self._editor.setText(unicode(value)) + + def getValue(self): + return unicode(self._editor.text()) + + def setupUI(self): + self._editor = QtGui.QLineEdit(self) + self._validator = ItemValidator(self, self._item) + self._editor.setValidator(self._validator) + l = QtGui.QHBoxLayout(self) + l.setContentsMargins(0,0,0,0) + l.addWidget(self._editor, 1) + self.setFocusProxy(self._editor) + +class IntEditor(BaseEditor): + _accepts = "IntItem" + def setupUI(self): + self._editor = QtGui.QSpinBox(self) + self._editor.setMinimum(self._item._min) + self._editor.setMaximum(self._item._max) + l = QtGui.QHBoxLayout(self) + l.setContentsMargins(0,0,0,0) + l.addWidget(self._editor, 1) + self.setFocusProxy(self._editor) + + def setValue(self, value): + self._editor.setValue(int(value)) + + def getValue(self): + return self._editor.value() + +class ColorEditor(BaseEditor): + _accepts = "ColorItem" + def setupUI(self): + self._editor_pix = QtGui.QPixmap(40,30) + self._editor_pix.fill(QtGui.QColor('white')) + + self._editor_btn = QtGui.QPushButton("") + self._editor_btn.setFlat(True) + self._editor_btn.setFocusPolicy(Qt.NoFocus) + self._editor_btn.setIcon(QtGui.QIcon(self._editor_pix)) + + self._editor_edt = QtGui.QLineEdit() + self._editor_edt.setInputMask(r"\#HHHHHHhh") + fm = QtGui.QApplication.fontMetrics() + w = fm.width("#FFFFFFFF ") + self._editor_edt.setMaximumWidth(w) + + + l = QtGui.QHBoxLayout(self) + l.setContentsMargins(0,0,0,0) + l.addWidget(self._editor_edt) + l.addWidget(self._editor_btn) + l.addStretch(1) + self.setFocusProxy(self._editor_edt) + assert self.connect(self._editor_btn, SIGNAL("pressed()"), + self.chooseColor) + assert self.connect(self._editor_edt, SIGNAL("editingFinished()"), + self.colorEdited) + + def setValue(self, value): + if isinstance(value, tuple): + color = self._item._type(*value) + elif isinstance(value, self._item._type): + color = value + elif isinstance(value, long): + color = self._item._type(value) + alpha = value >> 24 + color.setAlpha(alpha) + else: + color = self._item._type(value) + + rgba = color.getRgb() + colorname = ("#"+"%02X"*4)%rgba + self._rgba = rgba + self._editor_pix.fill(color) + self._editor_btn.setIcon(QtGui.QIcon(self._editor_pix)) + self._editor_edt.setText(colorname) + + def getValue(self): + return self._item._type(*self._rgba) + + def colorEdited(self): + val = unicode(self._editor_edt.text()) + if len(val) == 7: + val += "FF" # miss alpha channel + val = val[1:] + val = [val[2*i:2*i+2] for i in range(len(val)/2)] + val = [int(x, 16) for x in val] + + self._rgba = tuple(val) + self.setValue(self._rgba) + + + def chooseColor(self): + newcolor, ok = QtGui.QColorDialog.getRgba(self.getValue().rgba(), self) + if ok: + self.setValue(newcolor) + +class PreferencesEditor(QtGui.QDialog, form_class): + def __init__(self, preferences, parent=None): + QtGui.QDialog.__init__(self, parent) + self.setupUi(self) + self._prefs = preferences + self.buildUI() + + def buildUI(self): + mainw = self.centralTab + for i in range(mainw.count()): + mainw.removeTab(0) + + eds = {} + self._editors = eds + + wr = WidgetRegister() + if len(self._prefs.groups)>1: + for group in self._prefs.groups: + if group is None: + continue + w = QtGui.QWidget(mainw) + mainw.addTab(w, group) + g = QtGui.QGridLayout(w) + g.setVerticalSpacing(2) + for i, k in enumerate(self._prefs.keys(group)): + name = self._prefs.getName(k) + item = self._prefs.getItem(k) + if not name: + name = k + l = QtGui.QLabel(name, w) + g.addWidget(l, i, 0) + if self._prefs.getDescription(k): + l.setToolTip(self._prefs.getDescription(k)) + wcls = wr.getWidget(item) + e = wcls(w, item) + eds[k] = e + g.addWidget(e, i, 1) + val = self._prefs.getPref(k) + if val is None: + val = '' + e.setValue(val) + + # add blank space + g.addWidget(QtGui.QWidget(w), i+1, 0) + g.setRowStretch(i+1,1) + g.setColumnStretch(1,1) + + def accept(self): + p=self._prefs + for k in self._editors: + newval = self._editors[k].getValue() + p.setPref(k, newval) + return QtGui.QDialog.accept(self) + +if __name__ == '__main__': + from qpreferences import AbstractPreferences, UnicodeItem, IntItem, BaseItem + from qpreferences import ColorItem + class TestPreferences(AbstractPreferences): + ORGANISATION="Logilab" + APPLICATION="test_qpref_editor" + + device = UnicodeItem('/dev/ttyUSB0', name="the device", + group="GPIB settings") + address = IntItem(5, name="address", + description="GPIB address of the plotter", + group="GPIB settings", + min=0, max=16) + other = UnicodeItem('toto', name="other stuff", + group="General") + color = ColorItem(default='red',name="Colour", + group="General") + _pos = BaseItem(None) + _size = BaseItem(None) + _appState = BaseItem(None) + + a = QtGui.QApplication([]) + + prefs = TestPreferences() + w = PreferencesEditor(prefs) + w.show() + a.exec_()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/qt4/resources.qrc Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/" > + <file>icons/led_green.svg</file> + <file>icons/led_green_off.svg</file> + <file>icons/led_red.svg</file> + <file>icons/led_red_off.svg</file> + </qresource> +</RCC>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pygpibtoolkit/tools.py Tue Feb 05 18:59:29 2008 +0100 @@ -0,0 +1,81 @@ +# +""" +Helper functions and classes +""" +import re + +def str_num_cmp(s1,s2): + """ + string comparator function that will put 'toto_10' after 'toto_2' + Also works for strings like 'toto_1_et_23er25'. + """ + r = re.compile(r'((?<=\d)\D+|(?<=\D)\d+)') + r1= r.split(s1) + r2= r.split(s2) + r1=[not x.isdigit() and x or int(x) for x in r1 if x!=''] + r2=[not x.isdigit() and x or int(x) for x in r2 if x!=''] + return cmp(r1,r2) + + +class AbstractRegister(object): + _instance = None + _registered_type = None + def __new__(cls): + # implements a singleton + if cls._instance is None: + #debug('Instanciating %s', cls.__name__) + cls._instance = super(AbstractRegister, cls).__new__(cls) + cls._instance.registered = {} + cls._instance.accepts = set() + return cls._instance + + def add(self, cls): + assert issubclass(cls, self._registered_type) + if cls is self._registered_type: + return + if cls._accepts is None: + return + #assert isinstance(cls._accepts, (basestring, tuple)) + + #debug("Registerered %s for %s", cls.__name__) + if not isinstance(cls._accepts, tuple): + cls._accepts = (cls._accepts,) + for key in cls._accepts: + key = self._get_typ(key) + #debug(" new key = %s", key) + self.registered.setdefault(key, []).append(cls) + self.accepts.add(key) + + def _get_typ(self, typ): + if not isinstance(typ, basestring): + if not isinstance(typ, type): + return None + typ = typ.__name__ + return typ + + def __contains__(self, val): + val = self._get_typ(val) + return val in self.accepts + + def get_class(self, typ): + item = typ + if not isinstance(typ, type): + typ = typ.__class__ + name = typ.__name__ + #debug("Looking a widget for %s", typ.__name__) + orig_typ = typ + while typ is not None: + if typ.__name__ in self.registered: + for w in self.registered[typ.__name__]: + if w._filter is None or w._filter(item): + #debug("Widget for %s is %s", typ.__name__, w) + return w#self.registered[typ.__name__] + if typ.__bases__: + typ = typ.__bases__[0] + if typ == object: + typ = None + elif typ.__name__ in ("ObjectItem_", "AbstractListItem_"): + typ = orig_typ._type + else: + typ = None + raise ValueError('No registered class for %s' % (name))
--- a/qt4/icons/led_green.svg Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="278.25241" - height="278.25241" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.45.1" - sodipodi:docbase="/home/david/Electronic/HP3562/icons" - sodipodi:docname="led_green.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.0"> - <defs - id="defs4"> - <linearGradient - inkscape:collect="always" - id="linearGradient4117"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop4119" /> - <stop - style="stop-color:#ffffff;stop-opacity:0;" - offset="1" - id="stop4121" /> - </linearGradient> - <linearGradient - id="linearGradient3136"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop3138" /> - <stop - style="stop-color:#00ff00;stop-opacity:0;" - offset="1" - id="stop3140" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3136" - id="radialGradient3142" - cx="327.41925" - cy="34.101982" - fx="327.41925" - fy="34.101982" - r="130.1324" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.5695707e-7,-3.2469858,2.296245,6.8708969e-6,366.91049,1504.183)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4123" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4129" - gradientUnits="userSpaceOnUse" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="0.48082306" - inkscape:cx="372.04724" - inkscape:cy="207.1213" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:window-width="996" - inkscape:window-height="714" - inkscape:window-x="25" - inkscape:window-y="4" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Calque 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-175.95852,-171.79899)"> - <rect - style="opacity:1;fill:#00ff00;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" - id="rect2160" - width="259.97089" - height="259.97089" - x="185.09927" - y="180.93974" /> - <rect - y="176.38744" - x="180.54697" - height="269.0755" - width="269.0755" - id="rect2162" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#00c40b;stroke-width:9.17689896;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - y="180.93974" - x="185.09927" - height="259.97089" - width="259.97089" - id="rect3134" - style="opacity:1;fill:url(#radialGradient3142);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" /> - <path - style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" - d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " - id="path3144" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path3146" - sodipodi:cx="303.646" - sodipodi:cy="129.98544" - sodipodi:rx="0" - sodipodi:ry="7.2791853" - d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> - </g> -</svg>
--- a/qt4/icons/led_green_off.svg Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="278.25241" - height="278.25241" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.45.1" - sodipodi:docbase="/home/david/Electronic/HP3562/icons" - sodipodi:docname="led_green_off.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.0"> - <defs - id="defs4"> - <linearGradient - id="linearGradient4117"> - <stop - style="stop-color:#acacac;stop-opacity:0.68783069;" - offset="0" - id="stop4119" /> - <stop - style="stop-color:#ffffff;stop-opacity:0;" - offset="1" - id="stop4121" /> - </linearGradient> - <linearGradient - id="linearGradient3136"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop3138" /> - <stop - style="stop-color:#00ff00;stop-opacity:0;" - offset="1" - id="stop3140" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4129" - gradientUnits="userSpaceOnUse" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="0.48082306" - inkscape:cx="372.04724" - inkscape:cy="-64.793696" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:window-width="996" - inkscape:window-height="714" - inkscape:window-x="22" - inkscape:window-y="21" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Calque 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-175.95852,-171.79899)"> - <rect - style="opacity:1;fill:#009900;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" - id="rect2160" - width="259.97089" - height="259.97089" - x="185.09927" - y="180.93974" /> - <rect - y="176.38744" - x="180.54697" - height="269.0755" - width="269.0755" - id="rect2162" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#008304;stroke-width:9.17689896;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <path - style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" - d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " - id="path3144" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path3146" - sodipodi:cx="303.646" - sodipodi:cy="129.98544" - sodipodi:rx="0" - sodipodi:ry="7.2791853" - d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> - </g> -</svg>
--- a/qt4/icons/led_red.svg Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="278.25241" - height="278.25241" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.45.1" - sodipodi:docbase="/home/david/Electronic/HP3562/icons" - sodipodi:docname="led_red.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.0"> - <defs - id="defs4"> - <linearGradient - inkscape:collect="always" - id="linearGradient4117"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop4119" /> - <stop - style="stop-color:#ffffff;stop-opacity:0;" - offset="1" - id="stop4121" /> - </linearGradient> - <linearGradient - id="linearGradient3136"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop3138" /> - <stop - style="stop-color:#ff0000;stop-opacity:0;" - offset="1" - id="stop3140" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3136" - id="radialGradient3142" - cx="327.41925" - cy="34.101982" - fx="327.41925" - fy="34.101982" - r="130.1324" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.5695707e-7,-3.2469858,2.296245,6.8708969e-6,366.91049,1504.183)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4123" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4129" - gradientUnits="userSpaceOnUse" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="0.96164611" - inkscape:cx="326.38382" - inkscape:cy="89.862869" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:window-width="996" - inkscape:window-height="714" - inkscape:window-x="11" - inkscape:window-y="13" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Calque 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-175.95852,-171.79899)"> - <rect - style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" - id="rect2160" - width="259.97089" - height="259.97089" - x="185.09927" - y="180.93974" /> - <rect - y="176.38744" - x="180.54697" - height="269.0755" - width="269.0755" - id="rect2162" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ff0000;stroke-width:9.17689896;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <path - style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" - d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " - id="path3144" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path3146" - sodipodi:cx="303.646" - sodipodi:cy="129.98544" - sodipodi:rx="0" - sodipodi:ry="7.2791853" - d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> - <rect - y="180.93974" - x="185.09927" - height="259.97089" - width="259.97089" - id="rect3134" - style="opacity:1;fill:url(#radialGradient3142);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" /> - </g> -</svg>
--- a/qt4/icons/led_red_off.svg Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="278.2525" - height="278.2525" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.45.1" - sodipodi:docbase="/home/david/Electronic/HP3562/icons" - sodipodi:docname="led_red_off.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.0"> - <defs - id="defs4"> - <linearGradient - inkscape:collect="always" - id="linearGradient4117"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop4119" /> - <stop - style="stop-color:#ffffff;stop-opacity:0;" - offset="1" - id="stop4121" /> - </linearGradient> - <linearGradient - id="linearGradient3136"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop3138" /> - <stop - style="stop-color:#aa0000;stop-opacity:0;" - offset="1" - id="stop3140" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3136" - id="radialGradient3142" - cx="327.41925" - cy="34.101982" - fx="327.41925" - fy="34.101982" - r="130.1324" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.5695707e-7,-3.2469858,2.296245,6.8708969e-6,366.91049,1504.183)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4123" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient4129" - gradientUnits="userSpaceOnUse" - x1="260.95242" - y1="121.18842" - x2="488.65955" - y2="351.48065" - gradientTransform="matrix(1.2457226,0,0,1.2615854,-176.7768,-90.496508)" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="0.96164611" - inkscape:cx="326.38382" - inkscape:cy="69.71549" - inkscape:document-units="px" - inkscape:current-layer="layer1" - inkscape:window-width="996" - inkscape:window-height="714" - inkscape:window-x="11" - inkscape:window-y="13" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Calque 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-175.95847,-171.79894)"> - <rect - y="180.93974" - x="185.09927" - height="259.97089" - width="259.97089" - id="rect3134" - style="opacity:1;fill:url(#radialGradient3142);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - style="opacity:1;fill:#a20000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.29390666;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29390666, 0.29390666;stroke-dashoffset:0;stroke-opacity:1" - id="rect2160" - width="259.97089" - height="259.97089" - x="185.09927" - y="180.93974" /> - <rect - y="176.38744" - x="180.54697" - height="269.0755" - width="269.0755" - id="rect2162" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#950000;stroke-width:9.17700005;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <path - style="fill:url(#linearGradient4129);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" - d="M 185.09375,180.9375 L 185.09375,352.5625 C 190.93752,352.79982 196.83856,352.9375 202.78125,352.9375 C 312.80014,352.9375 406.95049,311.61044 445.0625,253.25 L 445.0625,180.9375 L 185.09375,180.9375 z " - id="path3144" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path3146" - sodipodi:cx="303.646" - sodipodi:cy="129.98544" - sodipodi:rx="0" - sodipodi:ry="7.2791853" - d="M 303.646 129.98544 A 0 7.2791853 0 1 1 303.646,129.98544 A 0 7.2791853 0 1 1 303.646 129.98544 z" /> - </g> -</svg>
--- a/qt4/qpreferences.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -import os -from PyQt4 import QtCore, QtGui, uic -from tools import AbstractRegister - -def fromVariant(v): - _cvrts = {0: lambda x:None, - 1: lambda x:x.toBool(), - 2: lambda x:x.toInt()[0], - 6: lambda x:x.toDouble()[0], - 10: lambda x:unicode(x.toString()), - 12: lambda x:x.toByteArray(), - 21: lambda x:x.toSize(), - 22: lambda x:x.toSizeF(), - 25: lambda x:x.toPoint(), - 26: lambda x:x.toPointF(), - 64: lambda x:QtGui.QFont(x), - 67: lambda x:QtGui.QColor(x), - } - t = v.userType() - return _cvrts[t](v) - -class PreferenceMetaclass(type): - _widgets = {} - def __init__(cls, name, bases, dct): - # called at class creation - super(type, cls).__init__(name, bases, dct) - if name != "BaseItem": - ItemRegister().add(cls) - -class BaseItem(object): - #__metaclass__ = PreferenceMetaclass - _id = 0 - def __init__(self, default=None, name=None, description=None, group=None): - self._default = default - self._id = BaseItem._id - self._name = name - self._description = description - self._group = group - BaseItem._id += 1 - - def validate(self, value): - return True - - def __get__(self, obj, cls): - if obj is None: #when called from the class, return the Item itself - return self - try: - return obj.getPref(self._id) - except Exception, e: - return None - - def __set__(self, obj, value): - obj.setPref(self._id, value) - -class ItemRegister(AbstractRegister): - _registered_type = BaseItem - getItem = AbstractRegister.get_class - -class PointItem(BaseItem): - _type = QtCore.QPoint - -class SizeItem(BaseItem): - _type = QtCore.QSize - -class ByteArrayItem(BaseItem): - _type = QtCore.QByteArray - -class UnicodeItem(BaseItem): - _type = unicode - def validate(self, value): - return isinstance(value, basestring) - -class IntItem(BaseItem): - _type = int - def __init__(self, default=None, name=None, description=None, group=None, min=None, max=None): - BaseItem.__init__(self, default, name, description, group) - self._min = min - self._max = max - - def validate(self, value): - try: - value = self._type(value) - except: - return False - if self._min is not None and value<self._min: - return False - if self._max is not None and value>self._max: - return False - return True - -class ColorItem(BaseItem): - _type = QtGui.QColor - - def validate(self, value): - try: - self._type(value) - return True - except: - return False - -class AbstractPreferences(QtCore.QObject): - def __init__(self): - QtCore.QObject.__init__(self) - self._settings = QtCore.QSettings(QtCore.QSettings.UserScope, - self.ORGANISATION, self.APPLICATION) - self._prefs = {} - self.groups = [] - keys = [] - for k in dir(self.__class__): - item = self.getItem(k) - if isinstance(item, BaseItem): - keys.append((k,item)) - keys.sort(key=lambda x: x[1]._id) - for k, item in keys: - self._prefs[item._id] = k - if item._group not in self.groups: - self.groups.append(item._group) - - def getItem(self, key): - return getattr(self.__class__, key) - #return self._prefs.get(key, None) - - def getPref(self, key): - key = self._prefs.get(key, key) - default = getattr(self.__class__, key)._default - if default is not None: - default = QtCore.QVariant(default) - else: - default = QtCore.QVariant() - val = fromVariant(self._settings.value(key, default)) - return val - - def setPref(self, key, value): - key = self._prefs.get(key, key) - self._settings.setValue(key, QtCore.QVariant(value)) - - def keys(self, group=None): - return [k for k in self._prefs.values() if not k.startswith('_') and self.getItem(k)._group == group] - - def getName(self, key): - item = getattr(self.__class__, key) - return item._name - - def getDescription(self, key): - item = getattr(self.__class__, key) - return item._description - - def __getitem__(self, key): - return getattr(self, key) -
--- a/qt4/qpreferences_dialog.ui Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -<ui version="4.0" > - <class>PreferencesDialog</class> - <widget class="QDialog" name="PreferencesDialog" > - <property name="geometry" > - <rect> - <x>0</x> - <y>0</y> - <width>400</width> - <height>300</height> - </rect> - </property> - <property name="windowTitle" > - <string>Dialog</string> - </property> - <layout class="QVBoxLayout" > - <item> - <widget class="QTabWidget" name="centralTab" > - <property name="currentIndex" > - <number>0</number> - </property> - <widget class="QWidget" name="tab" > - <attribute name="title" > - <string>Tab 1</string> - </attribute> - </widget> - </widget> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox" > - <property name="orientation" > - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons" > - <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>PreferencesDialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel" > - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel" > - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>PreferencesDialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel" > - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel" > - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> -</ui>
--- a/qt4/resources.qrc Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -<RCC> - <qresource prefix="/" > - <file>icons/led_green.svg</file> - <file>icons/led_green_off.svg</file> - <file>icons/led_red.svg</file> - <file>icons/led_red_off.svg</file> - </qresource> -</RCC>
--- a/tools.py Mon Feb 04 19:49:48 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -# -""" -Helper functions and classes -""" -import re - -def str_num_cmp(s1,s2): - """ - string comparator function that will put 'toto_10' after 'toto_2' - Also works for strings like 'toto_1_et_23er25'. - """ - r = re.compile(r'((?<=\d)\D+|(?<=\D)\d+)') - r1= r.split(s1) - r2= r.split(s2) - r1=[not x.isdigit() and x or int(x) for x in r1 if x!=''] - r2=[not x.isdigit() and x or int(x) for x in r2 if x!=''] - return cmp(r1,r2) - - -class AbstractRegister(object): - _instance = None - _registered_type = None - def __new__(cls): - # implements a singleton - if cls._instance is None: - #debug('Instanciating %s', cls.__name__) - cls._instance = super(AbstractRegister, cls).__new__(cls) - cls._instance.registered = {} - cls._instance.accepts = set() - return cls._instance - - def add(self, cls): - assert issubclass(cls, self._registered_type) - if cls is self._registered_type: - return - if cls._accepts is None: - return - #assert isinstance(cls._accepts, (basestring, tuple)) - - #debug("Registerered %s for %s", cls.__name__) - if not isinstance(cls._accepts, tuple): - cls._accepts = (cls._accepts,) - for key in cls._accepts: - key = self._get_typ(key) - #debug(" new key = %s", key) - self.registered.setdefault(key, []).append(cls) - self.accepts.add(key) - - def _get_typ(self, typ): - if not isinstance(typ, basestring): - if not isinstance(typ, type): - return None - typ = typ.__name__ - return typ - - def __contains__(self, val): - val = self._get_typ(val) - return val in self.accepts - - def get_class(self, typ): - item = typ - if not isinstance(typ, type): - typ = typ.__class__ - name = typ.__name__ - #debug("Looking a widget for %s", typ.__name__) - orig_typ = typ - while typ is not None: - if typ.__name__ in self.registered: - for w in self.registered[typ.__name__]: - if w._filter is None or w._filter(item): - #debug("Widget for %s is %s", typ.__name__, w) - return w#self.registered[typ.__name__] - if typ.__bases__: - typ = typ.__bases__[0] - if typ == object: - typ = None - elif typ.__name__ in ("ObjectItem_", "AbstractListItem_"): - typ = orig_typ._type - else: - typ = None - raise ValueError('No registered class for %s' % (name))