@@ -1,81 +1,84 | |||
|
1 | 1 | import mpldriver |
|
2 | 2 | |
|
3 | 3 | class Figure: |
|
4 | 4 | axesList = None |
|
5 | 5 | width = None |
|
6 | 6 | height = None |
|
7 | 7 | def __init__(self): |
|
8 | 8 | pass |
|
9 | 9 | |
|
10 | 10 | def init(self, idfigure, wintitle, width, height, nplots): |
|
11 | 11 | self.idfigure = idfigure |
|
12 | 12 | self.wintitle = wintitle |
|
13 | 13 | self.width = width |
|
14 | 14 | self.height = height |
|
15 | 15 | self.nplots = nplots |
|
16 | mpldriver.init(idfigure, wintitle, width, height) | |
|
16 | self.fig = mpldriver.init(idfigure, wintitle, width, height) | |
|
17 | 17 | |
|
18 | 18 | self.axesList = [] |
|
19 | 19 | |
|
20 | 20 | def setTitle(self, title): |
|
21 | 21 | mpldriver.setTitle(self.idfigure, title) |
|
22 | 22 | |
|
23 | def setWinTitle(self,title): | |
|
24 | mpldriver.setWinTitle(fig=self.fig, title=title) | |
|
25 | ||
|
23 | 26 | def setTextFromAxes(self, title): |
|
24 | 27 | mpldriver.setTextFromAxes(self.idfigure, self.axesList[0].ax, title) |
|
25 | 28 | |
|
26 | 29 | def makeAxes(self, nrow, ncol, xpos, ypos, colspan, rowspan): |
|
27 | 30 | ax = mpldriver.makeAxes(self.idfigure, nrow, ncol, xpos, ypos, colspan, rowspan) |
|
28 | 31 | axesObj = Axes(ax) |
|
29 | 32 | self.axesList.append(axesObj) |
|
30 | 33 | |
|
31 | 34 | def draw(self): |
|
32 | 35 | mpldriver.draw(self.idfigure) |
|
33 | 36 | |
|
34 | 37 | def run(self): |
|
35 | 38 | pass |
|
36 | 39 | |
|
37 | 40 | |
|
38 | 41 | class Axes: |
|
39 | 42 | firsttime = None |
|
40 | 43 | ax = None |
|
41 | 44 | mesh = None |
|
42 | 45 | |
|
43 | 46 | def __init__(self, ax): |
|
44 | 47 | self.firsttime = True |
|
45 | 48 | self.ax = ax |
|
46 | 49 | self.mesh = None |
|
47 | 50 | |
|
48 | 51 | def pline(self, x, y, xmin, xmax, ymin, ymax, xlabel, ylabel, title): |
|
49 | 52 | |
|
50 | 53 | mpldriver.pline(ax=self.ax, |
|
51 | 54 | x=x, |
|
52 | 55 | y=y, |
|
53 | 56 | xmin=xmin, |
|
54 | 57 | xmax=xmax, |
|
55 | 58 | ymin=ymin, |
|
56 | 59 | ymax=ymax, |
|
57 | 60 | xlabel=xlabel, |
|
58 | 61 | ylabel=ylabel, |
|
59 | 62 | title=title, |
|
60 | 63 | firsttime=self.firsttime) |
|
61 | 64 | |
|
62 | 65 | self.firsttime = False |
|
63 | 66 | |
|
64 | 67 | def pcolor(self, x, y, z, xmin, xmax, ymin, ymax, zmin, zmax, xlabel, ylabel, title): |
|
65 | 68 | meshfromaxes=mpldriver.pcolor(ax=self.ax, |
|
66 | 69 | x=x, |
|
67 | 70 | y=y, |
|
68 | 71 | z=z, |
|
69 | 72 | xmin=xmin, |
|
70 | 73 | xmax=xmax, |
|
71 | 74 | ymin=ymin, |
|
72 | 75 | ymax=ymax, |
|
73 | 76 | zmin=zmin, |
|
74 | 77 | zmax=zmax, |
|
75 | 78 | xlabel=xlabel, |
|
76 | 79 | ylabel=ylabel, |
|
77 | 80 | title=title, |
|
78 | 81 | firsttime=self.firsttime, |
|
79 | 82 | mesh=self.mesh) |
|
80 | 83 | self.mesh = meshfromaxes |
|
81 | 84 | self.firsttime = False |
@@ -1,75 +1,80 | |||
|
1 | 1 | import matplotlib |
|
2 | 2 | matplotlib.use("TKAgg") |
|
3 | 3 | import matplotlib.pyplot |
|
4 | 4 | import scitools.numpyutils |
|
5 | 5 | from mpl_toolkits.axes_grid1 import make_axes_locatable |
|
6 | 6 | |
|
7 | 7 | def init(idfigure, wintitle, width, height): |
|
8 | 8 | matplotlib.pyplot.ioff() |
|
9 | 9 | fig = matplotlib.pyplot.matplotlib.pyplot.figure(num=idfigure, facecolor="w") |
|
10 | 10 | fig.canvas.manager.set_window_title(wintitle) |
|
11 | 11 | fig.canvas.manager.resize(width,height) |
|
12 | 12 | matplotlib.pyplot.ion() |
|
13 | ||
|
13 | return fig | |
|
14 | ||
|
15 | def setWinTitle(fig, title): | |
|
16 | fig.canvas.manager.set_window_title(title) | |
|
17 | ||
|
14 | 18 | def setTextFromAxes(idfigure, ax, title): |
|
15 | 19 | fig = matplotlib.pyplot.figure(idfigure) |
|
16 | 20 | ax.annotate(title, xy=(.1, .99), |
|
17 | 21 | xycoords='figure fraction', |
|
18 | 22 | horizontalalignment='left', verticalalignment='top', |
|
19 | 23 | fontsize=10) |
|
20 | 24 | |
|
21 | 25 | def setTitle(idfigure, title): |
|
22 | 26 | fig = matplotlib.pyplot.figure(idfigure) |
|
23 | 27 | fig.suptitle(title) |
|
24 | 28 | |
|
25 | 29 | def makeAxes(idfigure, nrow, ncol, xpos, ypos, colspan, rowspan): |
|
26 | 30 | fig = matplotlib.pyplot.figure(idfigure) |
|
27 | 31 | ax = matplotlib.pyplot.subplot2grid((nrow, ncol), (xpos, ypos), colspan=colspan, rowspan=rowspan) |
|
28 | 32 | return ax |
|
29 | 33 | |
|
30 | 34 | def pline(ax, x, y, xmin, xmax, ymin, ymax, xlabel, ylabel, title, firsttime): |
|
31 | 35 | if firsttime: |
|
32 | 36 | ax.plot(x, y) |
|
33 | 37 | ax.set_xlim([xmin,xmax]) |
|
34 | 38 | ax.set_ylim([ymin,ymax]) |
|
35 | 39 | ax.set_xlabel(xlabel, size=8) |
|
36 | 40 | ax.set_ylabel(ylabel, size=8) |
|
37 | 41 | ax.set_title(title, size=10) |
|
38 | 42 | matplotlib.pyplot.tight_layout() |
|
39 | 43 | else: |
|
40 | 44 | ax.lines[0].set_data(x,y) |
|
41 | 45 | |
|
42 | 46 | def draw(idfigure): |
|
43 | 47 | fig = matplotlib.pyplot.figure(idfigure) |
|
44 | 48 | fig.canvas.draw() |
|
45 | 49 | |
|
46 | 50 | def pcolor(ax, x, y, z, xmin, xmax, ymin, ymax, zmin, zmax, xlabel, ylabel, title, firsttime, mesh): |
|
47 | 51 | if firsttime: |
|
48 | 52 | divider = make_axes_locatable(ax) |
|
49 | 53 | ax_cb = divider.new_horizontal(size="5%", pad=0.05) |
|
50 | 54 | fig1 = ax.get_figure() |
|
51 | 55 | fig1.add_axes(ax_cb) |
|
52 | 56 | |
|
53 | 57 | ax.set_xlim([xmin,xmax]) |
|
54 | 58 | ax.set_ylim([ymin,ymax]) |
|
55 | 59 | ax.set_xlabel(xlabel) |
|
56 | 60 | ax.set_ylabel(ylabel) |
|
57 | 61 | ax.set_title(title) |
|
58 | 62 | |
|
59 | imesh=ax.pcolormesh(x,y,z,vmin=zmin,vmax=zmax) | |
|
63 | imesh=ax.pcolormesh(x,y,z.T,vmin=zmin,vmax=zmax) | |
|
60 | 64 | matplotlib.pyplot.colorbar(imesh, cax=ax_cb) |
|
61 | 65 | ax_cb.yaxis.tick_right() |
|
62 | 66 | for tl in ax_cb.get_yticklabels(): |
|
63 | 67 | tl.set_visible(True) |
|
64 | 68 | ax_cb.yaxis.tick_right() |
|
65 | 69 | matplotlib.pyplot.tight_layout() |
|
66 | 70 | return imesh |
|
67 | 71 | else: |
|
68 | tmp = z[0:-1,0:-1] | |
|
69 | mesh.set_array(tmp.ravel()) | |
|
72 | z = z.T | |
|
73 | z = z[0:-1,0:-1] | |
|
74 | mesh.set_array(z.ravel()) | |
|
70 | 75 | |
|
71 | 76 | return mesh |
|
72 | 77 | |
|
73 | 78 | |
|
74 | 79 | |
|
75 | 80 | No newline at end of file |
@@ -1,182 +1,186 | |||
|
1 | 1 | import numpy |
|
2 | 2 | import datetime |
|
3 | 3 | from graphics.figure import * |
|
4 | 4 | |
|
5 | 5 | class SpectraPlot(Figure): |
|
6 | 6 | __isConfig = None |
|
7 | 7 | |
|
8 | 8 | def __init__(self): |
|
9 | 9 | self.__isConfig = False |
|
10 | 10 | self.width = 850 |
|
11 | 11 | self.height = 800 |
|
12 | 12 | |
|
13 | 13 | def getSubplots(self): |
|
14 | 14 | ncol = int(numpy.sqrt(self.nplots)+0.9) |
|
15 | 15 | nrow = int(self.nplots*1./ncol + 0.9) |
|
16 | 16 | return nrow, ncol |
|
17 | 17 | |
|
18 | 18 | |
|
19 | 19 | def setAxesWithOutProfiles(self, nrow, ncol): |
|
20 | 20 | colspan = 1 |
|
21 | 21 | rowspan = 1 |
|
22 | 22 | counter = 0 |
|
23 | 23 | |
|
24 | 24 | for y in range(nrow): |
|
25 | 25 | for x in range(ncol): |
|
26 | 26 | if counter < self.nplots: |
|
27 | # plt.subplot2grid((nrow, ncol), (y, x), colspan=colspan, rowspan=rowspan) | |
|
28 | 27 | self.makeAxes(nrow, ncol, y, x, colspan, rowspan) |
|
29 | 28 | counter += 1 |
|
30 | 29 | |
|
31 | 30 | def setAxesWithProfiles(self, nrow, ncol): |
|
32 | 31 | colspan = 1 |
|
33 | 32 | rowspan = 1 |
|
34 | 33 | factor = 2 |
|
35 | 34 | ncol = ncol*factor |
|
36 | 35 | counter = 0 |
|
37 | 36 | |
|
38 | 37 | for y in range(nrow): |
|
39 | 38 | for x in range(ncol): |
|
40 | 39 | if counter < self.nplots*factor: |
|
41 | 40 | # plt.subplot2grid((nrow, ncol), (y, x), colspan=colspan, rowspan=rowspan) |
|
42 | 41 | self.makeAxes(nrow, ncol, y, x, colspan, rowspan) |
|
43 | 42 | counter += 1 |
|
44 | 43 | |
|
45 | 44 | def setup(self, idfigure, wintitle, width, height, nplots, profile): |
|
46 | 45 | self.init(idfigure, wintitle, width, height, nplots) |
|
47 | 46 | |
|
48 | 47 | nrow,ncol = self.getSubplots() |
|
49 | 48 | |
|
50 | 49 | if profile: |
|
51 | 50 | self.setAxesWithProfiles(nrow, ncol) |
|
52 | 51 | else: |
|
53 | 52 | self.setAxesWithOutProfiles(nrow, ncol) |
|
54 | 53 | |
|
55 | 54 | def run(self, dataOut, idfigure, wintitle="", channelList=None, xmin=None, xmax=None, ymin=None, ymax=None, zmin=None, zmax=None, profile=False): |
|
56 | 55 | if dataOut.isEmpty(): |
|
57 | 56 | return None |
|
58 | 57 | |
|
59 | 58 | if channelList == None: |
|
60 | 59 | channelList = dataOut.channelList |
|
61 | 60 | |
|
62 | 61 | nplots = len(channelList) |
|
63 | 62 | |
|
64 | 63 | z = 10.*numpy.log10(dataOut.data_spc[channelList,:,:]) |
|
65 | 64 | |
|
66 | 65 | y = dataOut.heightList |
|
67 | 66 | |
|
68 | 67 | x = numpy.arange(dataOut.nFFTPoints) |
|
69 | 68 | |
|
70 | 69 | if not self.__isConfig: |
|
71 | 70 | self.setup(idfigure=idfigure, |
|
72 | 71 | wintitle=wintitle, |
|
73 | 72 | width=self.width, |
|
74 | 73 | height=self.height, |
|
75 | 74 | nplots=nplots, |
|
76 | 75 | profile=profile) |
|
77 | 76 | |
|
78 | 77 | if xmin == None: self.xmin = numpy.min(x) |
|
79 | 78 | if xmax == None: self.xmax = numpy.max(x) |
|
80 | 79 | if ymin == None: self.ymin = numpy.min(y) |
|
81 | 80 | if ymax == None: self.ymax = numpy.max(y) |
|
82 | 81 | if zmin == None: self.zmin = 0 |
|
83 | 82 | if zmax == None: self.zmax = 90 |
|
84 | 83 | |
|
85 | 84 | self.__isConfig = True |
|
86 | 85 | |
|
86 | thisDatetime = datetime.datetime.fromtimestamp(dataOut.utctime) | |
|
87 | dateTime = "%s"%(thisDatetime.strftime("%d-%b-%Y %H:%M:%S")) | |
|
88 | date = "%s"%(thisDatetime.strftime("%d-%b-%Y")) | |
|
89 | title = "Spectra: " + dateTime | |
|
90 | ||
|
91 | self.setWinTitle(title) | |
|
92 | ||
|
87 | 93 | ylabel = "Range[Km]" |
|
88 | 94 | |
|
89 | 95 | xlabel = "m/s" |
|
90 | 96 | |
|
91 | 97 | for i in range(len(self.axesList)): |
|
92 | 98 | title = "Channel %d"%i |
|
93 | 99 | axes = self.axesList[i] |
|
94 | 100 | z2 = z[i,:,:] |
|
95 | axes.pcolor(x, y, z, self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax, xlabel, ylabel, title) | |
|
101 | axes.pcolor(x, y, z2, self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax, xlabel, ylabel, title) | |
|
96 | 102 | |
|
97 | 103 | |
|
98 | 104 | self.draw() |
|
99 | 105 | |
|
100 | 106 | |
|
101 | 107 | |
|
102 | 108 | |
|
103 | 109 | |
|
104 | 110 | class Scope(Figure): |
|
105 | 111 | __isConfig = None |
|
106 | 112 | |
|
107 | 113 | def __init__(self): |
|
108 | 114 | self.__isConfig = False |
|
109 | 115 | self.width = 850 |
|
110 | 116 | self.height = 800 |
|
111 | 117 | |
|
112 | 118 | def getSubplots(self): |
|
113 | 119 | nrow = self.nplots |
|
114 | 120 | ncol = 3 |
|
115 | 121 | return nrow, ncol |
|
116 | 122 | |
|
117 | 123 | def setup(self, idfigure, wintitle, width, height, nplots): |
|
118 | 124 | self.init(idfigure, wintitle, width, height, nplots) |
|
119 | 125 | |
|
120 | 126 | nrow,ncol = self.getSubplots() |
|
121 | 127 | colspan = 3 |
|
122 | 128 | rowspan = 1 |
|
123 | 129 | |
|
124 | 130 | for i in range(nplots): |
|
125 | 131 | self.makeAxes(nrow, ncol, i, 0, colspan, rowspan) |
|
126 | 132 | |
|
127 | 133 | |
|
128 | 134 | |
|
129 | 135 | def run(self, dataOut, idfigure, wintitle="", channelList=None, xmin=None, xmax=None, ymin=None, ymax=None): |
|
130 | 136 | |
|
131 | 137 | if dataOut.isEmpty(): |
|
132 | 138 | return None |
|
133 | 139 | |
|
134 | 140 | if channelList == None: |
|
135 | 141 | channelList = dataOut.channelList |
|
136 | 142 | |
|
137 | 143 | nplots = len(channelList) |
|
138 | 144 | |
|
139 | 145 | y = dataOut.data[channelList,:] * numpy.conjugate(dataOut.data[channelList,:]) |
|
140 | 146 | y = y.real |
|
141 | 147 | |
|
142 | 148 | x = dataOut.heightList |
|
143 | 149 | |
|
144 | 150 | if not self.__isConfig: |
|
145 | 151 | self.setup(idfigure=idfigure, |
|
146 | 152 | wintitle=wintitle, |
|
147 | 153 | width=self.width, |
|
148 | 154 | height=self.height, |
|
149 | 155 | nplots=nplots) |
|
150 | 156 | |
|
151 | 157 | if xmin == None: self.xmin = numpy.min(x) |
|
152 | 158 | if xmax == None: self.xmax = numpy.max(x) |
|
153 | 159 | if ymin == None: self.ymin = numpy.min(y) |
|
154 | 160 | if ymax == None: self.ymax = numpy.max(y) |
|
155 | 161 | |
|
156 | 162 | self.__isConfig = True |
|
157 | 163 | |
|
158 | 164 | |
|
159 | 165 | |
|
160 | 166 | thisDatetime = datetime.datetime.fromtimestamp(dataOut.utctime) |
|
161 | 167 | dateTime = "%s"%(thisDatetime.strftime("%d-%b-%Y %H:%M:%S")) |
|
162 | 168 | date = "%s"%(thisDatetime.strftime("%d-%b-%Y")) |
|
163 |
|
|
|
164 | ||
|
165 | self.setTitle(title=figuretitle) | |
|
169 | title = "Scope: " + dateTime | |
|
166 | 170 | |
|
167 |
|
|
|
171 | self.setWinTitle(title) | |
|
168 | 172 | |
|
169 | 173 | ylabel = "Intensity" |
|
170 | 174 | |
|
171 | 175 | xlabel = "Range[Km]" |
|
172 | 176 | |
|
173 | 177 | for i in range(len(self.axesList)): |
|
174 | 178 | title = "Channel %d"%i |
|
175 | 179 | axes = self.axesList[i] |
|
176 | 180 | y2 = y[i,:] |
|
177 | 181 | axes.pline(x, y2, self.xmin, self.xmax, self.ymin, self.ymax, xlabel, ylabel, title) |
|
178 | 182 | |
|
179 | 183 | self.draw() |
|
180 | 184 | |
|
181 | 185 | |
|
182 | 186 | No newline at end of file |
@@ -1,103 +1,103 | |||
|
1 | 1 | """ |
|
2 | 2 | $Author$ |
|
3 | 3 | $Id$ |
|
4 | 4 | |
|
5 | 5 | """ |
|
6 | 6 | import datetime |
|
7 | 7 | from controller import * |
|
8 | 8 | from model import * |
|
9 | 9 | |
|
10 | 10 | |
|
11 | 11 | class Test(): |
|
12 | 12 | def __init__(self): |
|
13 | 13 | self.createObjects() |
|
14 | 14 | self.run() |
|
15 | 15 | |
|
16 | 16 | def createObjects(self): |
|
17 | 17 | |
|
18 | 18 | self.upConfig = controller.UPConf(id=1, name="voltageproc", type="voltage") |
|
19 | 19 | |
|
20 | 20 | opConf = self.upConfig.addOperation(name="init", priority=0) |
|
21 | 21 | |
|
22 | 22 | opConf1 = self.upConfig.addOperation(name="CohInt", priority=1, type="other") |
|
23 | 23 | opConf1.addParameter(name="nCohInt", value=100) |
|
24 | 24 | |
|
25 | 25 | opConf2 = self.upConfig.addOperation(name="Scope", priority=2, type="other") |
|
26 | 26 | opConf2.addParameter(name="idfigure", value=1) |
|
27 | 27 | |
|
28 | 28 | |
|
29 | 29 | self.upConfigSpc = controller.UPConf(id=2, name="spectraproc", type="spectra") |
|
30 | 30 | opConf = self.upConfigSpc.addOperation(name="init", priority=0) |
|
31 | 31 | opConf.addParameter(name="nFFTPoints", value=8) |
|
32 | 32 | |
|
33 | 33 | opConf3 = self.upConfigSpc.addOperation(name="SpectraPlot", priority=1, type="other") |
|
34 | 34 | opConf3.addParameter(name="idfigure", value=2) |
|
35 | 35 | |
|
36 | 36 | # opConf = self.upConfig.addOperation(name="selectChannels", priority=3) |
|
37 | 37 | # opConf.addParameter(name="channelList", value=[0,1]) |
|
38 | 38 | |
|
39 | 39 | |
|
40 | 40 | ######################################### |
|
41 | 41 | self.objR = jrodataIO.VoltageReader() |
|
42 | 42 | self.objP = jroprocessing.VoltageProc() |
|
43 | 43 | self.objSpc = jroprocessing.SpectraProc() |
|
44 | 44 | |
|
45 | 45 | self.objInt = jroprocessing.CohInt() |
|
46 | 46 | |
|
47 | 47 | self.objP.addOperation(self.objInt, opConf1.id) |
|
48 | 48 | |
|
49 | 49 | self.objScope = jroplot.Scope() |
|
50 | 50 | |
|
51 | 51 | self.objP.addOperation(self.objScope, opConf2.id) |
|
52 | 52 | |
|
53 | 53 | self.objSpcPlot = jroplot.SpectraPlot() |
|
54 | 54 | |
|
55 | 55 | self.objSpc.addOperation(self.objSpcPlot, opConf3.id) |
|
56 | 56 | |
|
57 | 57 | self.connect(self.objR, self.objP) |
|
58 | 58 | |
|
59 | 59 | self.connect(self.objP, self.objSpc) |
|
60 | 60 | |
|
61 | 61 | def connect(self, obj1, obj2): |
|
62 | 62 | obj2.setInput(obj1.getOutput()) |
|
63 | 63 | |
|
64 | 64 | def run(self): |
|
65 | 65 | |
|
66 | 66 | while(True): |
|
67 |
self.objR.run(path="/Users/dsuarez/Remote/ |
|
|
67 | self.objR.run(path="/Users/dsuarez/Remote/EW_DRIFTS2", | |
|
68 | 68 | startDate=datetime.date(2012,1,1), |
|
69 | 69 | endDate=datetime.date(2012,12,30), |
|
70 | 70 | startTime=datetime.time(0,0,0), |
|
71 | 71 | endTime=datetime.time(23,59,59), |
|
72 | 72 | set=0, |
|
73 | 73 | expLabel = "", |
|
74 | 74 | ext = None, |
|
75 | 75 | online = False) |
|
76 | 76 | |
|
77 | 77 | for opConf in self.upConfig.getOperationObjList(): |
|
78 | 78 | kwargs={} |
|
79 | 79 | for parm in opConf.getParameterObjList(): |
|
80 | 80 | kwargs[parm.name]=parm.value |
|
81 | 81 | |
|
82 | 82 | self.objP.call(opConf,**kwargs) |
|
83 | 83 | |
|
84 | 84 | ############################ |
|
85 | 85 | for opConfSpc in self.upConfigSpc.getOperationObjList(): |
|
86 | 86 | kwargs={} |
|
87 | 87 | for parm in opConfSpc.getParameterObjList(): |
|
88 | 88 | kwargs[parm.name]=parm.value |
|
89 | 89 | |
|
90 | 90 | self.objSpc.call(opConfSpc,**kwargs) |
|
91 | 91 | |
|
92 | 92 | if self.objR.flagNoMoreFiles: |
|
93 | 93 | break |
|
94 | 94 | |
|
95 | 95 | if self.objR.flagIsNewBlock: |
|
96 | 96 | print 'Block No %04d, Time: %s' %(self.objR.nTotalBlocks, |
|
97 | 97 | datetime.datetime.fromtimestamp(self.objR.basicHeaderObj.utc + self.objR.basicHeaderObj.miliSecond/1000.0),) |
|
98 | 98 | |
|
99 | 99 | |
|
100 | 100 | |
|
101 | 101 | |
|
102 | 102 | if __name__ == "__main__": |
|
103 | 103 | Test() No newline at end of file |
General Comments 0
You need to be logged in to leave comments.
Login now