Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: USBDevice mbed-rtos mbed
Fork of JoyStick by
main.cpp@5:a0bb17c379ce, 2016-06-22 (annotated)
- Committer:
- rvt
- Date:
- Wed Jun 22 12:50:16 2016 +0000
- Revision:
- 5:a0bb17c379ce
- Parent:
- 4:2cc58c173de8
Latest
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| rvt | 0:33bc88c4ab31 | 1 | #include "mbed.h" |
| rvt | 0:33bc88c4ab31 | 2 | #include "USBHID.h" |
| rvt | 0:33bc88c4ab31 | 3 | #include "USBJoystick.h" |
| rvt | 0:33bc88c4ab31 | 4 | #include "LowPassFilter.h" |
| rvt | 0:33bc88c4ab31 | 5 | #include "AnalogInFiltered.h" |
| rvt | 5:a0bb17c379ce | 6 | #include "AutoScale.h" |
| rvt | 4:2cc58c173de8 | 7 | #include "Button.h" |
| rvt | 0:33bc88c4ab31 | 8 | #include "rtos.h" |
| rvt | 0:33bc88c4ab31 | 9 | |
| rvt | 1:5b2ab44eb31f | 10 | // When set, it will send debug data over USB serial |
| rvt | 5:a0bb17c379ce | 11 | #define TTY_DEBUG false |
| rvt | 0:33bc88c4ab31 | 12 | |
| rvt | 0:33bc88c4ab31 | 13 | // Value that defines when to start sending data this prevents the noise sending loads's of data over HID |
| rvt | 5:a0bb17c379ce | 14 | #define DATA_CHANGE_TRIGGER 8 |
| rvt | 0:33bc88c4ab31 | 15 | |
| rvt | 1:5b2ab44eb31f | 16 | // Activity LED for HID data |
| rvt | 1:5b2ab44eb31f | 17 | #define HIDACTIVITYLED LED3 |
| rvt | 0:33bc88c4ab31 | 18 | |
| rvt | 0:33bc88c4ab31 | 19 | // Structure that hold's the dataset of the input's |
| rvt | 0:33bc88c4ab31 | 20 | Mutex analogValueMutex; |
| rvt | 0:33bc88c4ab31 | 21 | struct AnalogData { |
| rvt | 5:a0bb17c379ce | 22 | union { |
| rvt | 5:a0bb17c379ce | 23 | struct { |
| rvt | 5:a0bb17c379ce | 24 | uint32_t bit0:1; |
| rvt | 5:a0bb17c379ce | 25 | uint32_t bit1:1; |
| rvt | 5:a0bb17c379ce | 26 | uint32_t bit2:1; |
| rvt | 5:a0bb17c379ce | 27 | uint32_t bit3:1; |
| rvt | 5:a0bb17c379ce | 28 | uint32_t bit4:1; |
| rvt | 5:a0bb17c379ce | 29 | uint32_t bit5:1; |
| rvt | 5:a0bb17c379ce | 30 | uint32_t bit6:1; |
| rvt | 5:a0bb17c379ce | 31 | uint32_t bit7:1; |
| rvt | 5:a0bb17c379ce | 32 | uint32_t bit8:1; |
| rvt | 5:a0bb17c379ce | 33 | uint32_t bit9:1; |
| rvt | 5:a0bb17c379ce | 34 | uint32_t bit10:1; |
| rvt | 5:a0bb17c379ce | 35 | uint32_t bit11:1; |
| rvt | 5:a0bb17c379ce | 36 | uint32_t bit12:1; |
| rvt | 5:a0bb17c379ce | 37 | uint32_t bit13:1; |
| rvt | 5:a0bb17c379ce | 38 | uint32_t bit14:1; |
| rvt | 5:a0bb17c379ce | 39 | uint32_t bit15:1; |
| rvt | 5:a0bb17c379ce | 40 | uint32_t bit16:1; |
| rvt | 5:a0bb17c379ce | 41 | uint32_t bit17:1; |
| rvt | 5:a0bb17c379ce | 42 | uint32_t bit18:1; |
| rvt | 5:a0bb17c379ce | 43 | uint32_t bit19:1; |
| rvt | 5:a0bb17c379ce | 44 | uint32_t bit20:1; |
| rvt | 5:a0bb17c379ce | 45 | uint32_t bit21:1; |
| rvt | 5:a0bb17c379ce | 46 | uint32_t bit22:1; |
| rvt | 5:a0bb17c379ce | 47 | uint32_t bit23:1; |
| rvt | 5:a0bb17c379ce | 48 | uint32_t bit24:1; |
| rvt | 5:a0bb17c379ce | 49 | uint32_t bit25:1; |
| rvt | 5:a0bb17c379ce | 50 | uint32_t bit26:1; |
| rvt | 5:a0bb17c379ce | 51 | uint32_t bit27:1; |
| rvt | 5:a0bb17c379ce | 52 | uint32_t bit28:1; |
| rvt | 5:a0bb17c379ce | 53 | uint32_t bit29:1; |
| rvt | 5:a0bb17c379ce | 54 | uint32_t bit30:1; |
| rvt | 5:a0bb17c379ce | 55 | uint32_t bit31:1; |
| rvt | 5:a0bb17c379ce | 56 | }; |
| rvt | 5:a0bb17c379ce | 57 | uint32_t button; |
| rvt | 5:a0bb17c379ce | 58 | } buttons; |
| rvt | 0:33bc88c4ab31 | 59 | long value1; |
| rvt | 0:33bc88c4ab31 | 60 | long value2; |
| rvt | 0:33bc88c4ab31 | 61 | long value3; |
| rvt | 0:33bc88c4ab31 | 62 | long value4; |
| rvt | 0:33bc88c4ab31 | 63 | } analogData; |
| rvt | 0:33bc88c4ab31 | 64 | |
| rvt | 3:0742b0b42ac9 | 65 | |
| rvt | 1:5b2ab44eb31f | 66 | |
| rvt | 1:5b2ab44eb31f | 67 | |
| rvt | 1:5b2ab44eb31f | 68 | /** |
| rvt | 1:5b2ab44eb31f | 69 | Debug thread to show some values from the system over USB serial. |
| rvt | 1:5b2ab44eb31f | 70 | Ensure that TTY_DEBUG is defined so that these routines will get activated. |
| rvt | 1:5b2ab44eb31f | 71 | This is what I do to view the values on OSX within a terminal |
| rvt | 1:5b2ab44eb31f | 72 | $ cd /dev |
| rvt | 1:5b2ab44eb31f | 73 | $ ls | grep usbmodem |
| rvt | 1:5b2ab44eb31f | 74 | cu.usbmodemfa1232 |
| rvt | 1:5b2ab44eb31f | 75 | tty.usbmodemfa1232 |
| rvt | 1:5b2ab44eb31f | 76 | $ screen tty.usbmodemfa1232 |
| rvt | 1:5b2ab44eb31f | 77 | */ |
| rvt | 2:ae7a31a3c618 | 78 | const char *byte_to_binary16(int x) |
| rvt | 2:ae7a31a3c618 | 79 | { |
| rvt | 2:ae7a31a3c618 | 80 | static char b[17]; |
| rvt | 2:ae7a31a3c618 | 81 | b[0] = '\0'; |
| rvt | 2:ae7a31a3c618 | 82 | |
| rvt | 2:ae7a31a3c618 | 83 | int z; |
| rvt | 5:a0bb17c379ce | 84 | for (z = 32768; z > 0; z >>= 1) { |
| rvt | 2:ae7a31a3c618 | 85 | strcat(b, ((x & z) == z) ? "1" : "0"); |
| rvt | 2:ae7a31a3c618 | 86 | } |
| rvt | 2:ae7a31a3c618 | 87 | |
| rvt | 2:ae7a31a3c618 | 88 | return b; |
| rvt | 2:ae7a31a3c618 | 89 | } |
| rvt | 2:ae7a31a3c618 | 90 | |
| rvt | 2:ae7a31a3c618 | 91 | |
| rvt | 0:33bc88c4ab31 | 92 | void debug_thread(void const *args) |
| rvt | 0:33bc88c4ab31 | 93 | { |
| rvt | 2:ae7a31a3c618 | 94 | // Serial port for debug data |
| rvt | 2:ae7a31a3c618 | 95 | Serial pc(USBTX, USBRX); // tx, rx |
| rvt | 2:ae7a31a3c618 | 96 | |
| rvt | 1:5b2ab44eb31f | 97 | // Make a local copy |
| rvt | 1:5b2ab44eb31f | 98 | AnalogData previous; |
| rvt | 0:33bc88c4ab31 | 99 | while (true) { |
| rvt | 1:5b2ab44eb31f | 100 | // Lock and copy input values |
| rvt | 0:33bc88c4ab31 | 101 | analogValueMutex.lock(); |
| rvt | 5:a0bb17c379ce | 102 | const AnalogData localCopy = analogData; |
| rvt | 0:33bc88c4ab31 | 103 | analogValueMutex.unlock(); |
| rvt | 0:33bc88c4ab31 | 104 | |
| rvt | 0:33bc88c4ab31 | 105 | // Send to USB |
| rvt | 0:33bc88c4ab31 | 106 | pc.printf("\x1B[0;0H"); |
| rvt | 0:33bc88c4ab31 | 107 | pc.printf("Yoke and Pedals!\n\r"); |
| rvt | 5:a0bb17c379ce | 108 | pc.printf("Analog in p20: %d diff: %d \n\r",localCopy.value1,localCopy.value1-previous.value1); |
| rvt | 0:33bc88c4ab31 | 109 | pc.printf("Analog in p19: %d diff: %d \n\r",localCopy.value2,localCopy.value2-previous.value2); |
| rvt | 5:a0bb17c379ce | 110 | pc.printf("Buttons: %d \n\r",localCopy.buttons.button); |
| rvt | 1:5b2ab44eb31f | 111 | |
| rvt | 1:5b2ab44eb31f | 112 | // Make local copy so we can show diff version |
| rvt | 5:a0bb17c379ce | 113 | previous = localCopy; |
| rvt | 0:33bc88c4ab31 | 114 | Thread::wait(1000); |
| rvt | 0:33bc88c4ab31 | 115 | } |
| rvt | 0:33bc88c4ab31 | 116 | } |
| rvt | 3:0742b0b42ac9 | 117 | |
| rvt | 0:33bc88c4ab31 | 118 | |
| rvt | 2:ae7a31a3c618 | 119 | |
| rvt | 0:33bc88c4ab31 | 120 | void hid_thread(void const *args) |
| rvt | 0:33bc88c4ab31 | 121 | { |
| rvt | 1:5b2ab44eb31f | 122 | //USB HID JoyStick |
| rvt | 1:5b2ab44eb31f | 123 | USBJoystick joystick; |
| rvt | 1:5b2ab44eb31f | 124 | |
| rvt | 1:5b2ab44eb31f | 125 | // Activity led for HID data transmissions |
| rvt | 1:5b2ab44eb31f | 126 | DigitalOut hIDActivity(HIDACTIVITYLED); |
| rvt | 1:5b2ab44eb31f | 127 | |
| rvt | 0:33bc88c4ab31 | 128 | while (true) { |
| rvt | 0:33bc88c4ab31 | 129 | // Wait for analog in to have some data |
| rvt | 1:5b2ab44eb31f | 130 | hIDActivity=false; |
| rvt | 0:33bc88c4ab31 | 131 | Thread::signal_wait(0x1); |
| rvt | 1:5b2ab44eb31f | 132 | hIDActivity=true; |
| rvt | 0:33bc88c4ab31 | 133 | |
| rvt | 0:33bc88c4ab31 | 134 | // Make a local copy of the data |
| rvt | 0:33bc88c4ab31 | 135 | analogValueMutex.lock(); |
| rvt | 5:a0bb17c379ce | 136 | const AnalogData localCopy = analogData; |
| rvt | 0:33bc88c4ab31 | 137 | analogValueMutex.unlock(); |
| rvt | 0:33bc88c4ab31 | 138 | |
| rvt | 0:33bc88c4ab31 | 139 | // Update joystick's info |
| rvt | 0:33bc88c4ab31 | 140 | joystick.update( |
| rvt | 4:2cc58c173de8 | 141 | localCopy.value1, |
| rvt | 0:33bc88c4ab31 | 142 | localCopy.value2, |
| rvt | 0:33bc88c4ab31 | 143 | localCopy.value3, |
| rvt | 0:33bc88c4ab31 | 144 | localCopy.value4, |
| rvt | 5:a0bb17c379ce | 145 | localCopy.buttons.button); |
| rvt | 0:33bc88c4ab31 | 146 | |
| rvt | 0:33bc88c4ab31 | 147 | // Wait 50 ms to send a other USB update |
| rvt | 0:33bc88c4ab31 | 148 | Thread::wait(50); |
| rvt | 0:33bc88c4ab31 | 149 | } |
| rvt | 0:33bc88c4ab31 | 150 | } |
| rvt | 0:33bc88c4ab31 | 151 | |
| rvt | 0:33bc88c4ab31 | 152 | |
| rvt | 0:33bc88c4ab31 | 153 | |
| rvt | 0:33bc88c4ab31 | 154 | int main() |
| rvt | 0:33bc88c4ab31 | 155 | { |
| rvt | 5:a0bb17c379ce | 156 | analogData.buttons.button = 0; |
| rvt | 4:2cc58c173de8 | 157 | analogData.value1=0.; |
| rvt | 4:2cc58c173de8 | 158 | analogData.value2=0.; |
| rvt | 4:2cc58c173de8 | 159 | analogData.value3=0.; |
| rvt | 4:2cc58c173de8 | 160 | analogData.value4=0.; |
| rvt | 4:2cc58c173de8 | 161 | |
| rvt | 4:2cc58c173de8 | 162 | Button but5(p5, true, true); |
| rvt | 4:2cc58c173de8 | 163 | Button but6(p6, true, true); |
| rvt | 4:2cc58c173de8 | 164 | Button but7(p7, true, true); |
| rvt | 4:2cc58c173de8 | 165 | Button but8(p8, true, true); |
| rvt | 4:2cc58c173de8 | 166 | Button but9(p9, true, true); |
| rvt | 4:2cc58c173de8 | 167 | Button but10(p10, true, true); |
| rvt | 4:2cc58c173de8 | 168 | Button but11(p11, true, true); |
| rvt | 4:2cc58c173de8 | 169 | Button but12(p12, true, true); |
| rvt | 4:2cc58c173de8 | 170 | Button but13(p13, true, true); |
| rvt | 4:2cc58c173de8 | 171 | Button but14(p14, true, true); |
| rvt | 4:2cc58c173de8 | 172 | Button but15(p15, true, true); |
| rvt | 4:2cc58c173de8 | 173 | Button but16(p16, true, true); |
| rvt | 4:2cc58c173de8 | 174 | Button but17(p17, true, true); |
| rvt | 4:2cc58c173de8 | 175 | Button but21(p21, true, true); |
| rvt | 4:2cc58c173de8 | 176 | Button but22(p22, true, true); |
| rvt | 4:2cc58c173de8 | 177 | Button but23(p23, true, true); |
| rvt | 4:2cc58c173de8 | 178 | Button but24(p24, true, true); |
| rvt | 4:2cc58c173de8 | 179 | Button but25(p25, true, true); |
| rvt | 0:33bc88c4ab31 | 180 | |
| rvt | 3:0742b0b42ac9 | 181 | if (TTY_DEBUG) { |
| rvt | 3:0742b0b42ac9 | 182 | Thread _debugThread(debug_thread); |
| rvt | 5:a0bb17c379ce | 183 | } |
| rvt | 0:33bc88c4ab31 | 184 | |
| rvt | 0:33bc88c4ab31 | 185 | // Initialise moving average filters |
| rvt | 5:a0bb17c379ce | 186 | LowPassFilter lowPassFilter1(new AnalogFilterInterface(),0.99f); // The close the alpha value is to 1, the lower the cut-off frequency |
| rvt | 5:a0bb17c379ce | 187 | LowPassFilter lowPassFilter2(new AnalogFilterInterface(),0.99f); |
| rvt | 5:a0bb17c379ce | 188 | LowPassFilter lowPassFilter3(new AnalogFilterInterface(),0.96f); |
| rvt | 5:a0bb17c379ce | 189 | |
| rvt | 5:a0bb17c379ce | 190 | AutoScale autoScale1(&lowPassFilter1, -32768, 32767, 1.01); |
| rvt | 5:a0bb17c379ce | 191 | AutoScale autoScale2(&lowPassFilter2, -32768, 32767, 1.01); |
| rvt | 5:a0bb17c379ce | 192 | AutoScale autoScale3(&lowPassFilter3, -32768, 32767, 1.01); |
| rvt | 0:33bc88c4ab31 | 193 | |
| rvt | 0:33bc88c4ab31 | 194 | // Initialise analog input and tell it what fulters to use |
| rvt | 5:a0bb17c379ce | 195 | AnalogInFiltered ai1(&autoScale1, p20, DATA_CHANGE_TRIGGER); |
| rvt | 5:a0bb17c379ce | 196 | AnalogInFiltered ai2(&autoScale2, p19, DATA_CHANGE_TRIGGER); |
| rvt | 5:a0bb17c379ce | 197 | AnalogInFiltered ai3(&autoScale3, p18, DATA_CHANGE_TRIGGER); |
| rvt | 5:a0bb17c379ce | 198 | |
| rvt | 5:a0bb17c379ce | 199 | Thread _hid_thread(hid_thread); |
| rvt | 0:33bc88c4ab31 | 200 | while (true) { |
| rvt | 0:33bc88c4ab31 | 201 | // Measure analog in's |
| rvt | 5:a0bb17c379ce | 202 | ai1.setData(0); |
| rvt | 5:a0bb17c379ce | 203 | ai2.setData(0); |
| rvt | 5:a0bb17c379ce | 204 | ai3.setData(0); |
| rvt | 4:2cc58c173de8 | 205 | |
| rvt | 4:2cc58c173de8 | 206 | but5.measure(); |
| rvt | 4:2cc58c173de8 | 207 | but6.measure(); |
| rvt | 4:2cc58c173de8 | 208 | but7.measure(); |
| rvt | 4:2cc58c173de8 | 209 | but8.measure(); |
| rvt | 4:2cc58c173de8 | 210 | but9.measure(); |
| rvt | 4:2cc58c173de8 | 211 | but10.measure(); |
| rvt | 4:2cc58c173de8 | 212 | but11.measure(); |
| rvt | 4:2cc58c173de8 | 213 | but12.measure(); |
| rvt | 4:2cc58c173de8 | 214 | but13.measure(); |
| rvt | 4:2cc58c173de8 | 215 | but14.measure(); |
| rvt | 4:2cc58c173de8 | 216 | but15.measure(); |
| rvt | 4:2cc58c173de8 | 217 | but16.measure(); |
| rvt | 4:2cc58c173de8 | 218 | but17.measure(); |
| rvt | 5:a0bb17c379ce | 219 | |
| rvt | 4:2cc58c173de8 | 220 | but21.measure(); |
| rvt | 4:2cc58c173de8 | 221 | but22.measure(); |
| rvt | 4:2cc58c173de8 | 222 | but23.measure(); |
| rvt | 4:2cc58c173de8 | 223 | but24.measure(); |
| rvt | 4:2cc58c173de8 | 224 | but25.measure(); |
| rvt | 0:33bc88c4ab31 | 225 | |
| rvt | 0:33bc88c4ab31 | 226 | // test of any of the values have been changed, so we only update when data was actually changed |
| rvt | 5:a0bb17c379ce | 227 | const bool isChanged = |
| rvt | 5:a0bb17c379ce | 228 | ai1.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 229 | ai2.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 230 | ai3.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 231 | but5.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 232 | but6.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 233 | but7.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 234 | but8.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 235 | but9.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 236 | but10.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 237 | but11.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 238 | but12.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 239 | but13.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 240 | but14.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 241 | but15.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 242 | but16.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 243 | but17.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 244 | but21.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 245 | but22.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 246 | but23.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 247 | but24.getIsChanged() || |
| rvt | 5:a0bb17c379ce | 248 | but25.getIsChanged(); |
| rvt | 0:33bc88c4ab31 | 249 | if ( |
| rvt | 5:a0bb17c379ce | 250 | isChanged |
| rvt | 5:a0bb17c379ce | 251 | ) { |
| rvt | 0:33bc88c4ab31 | 252 | // Copy analog data to global data |
| rvt | 0:33bc88c4ab31 | 253 | analogValueMutex.lock(); |
| rvt | 5:a0bb17c379ce | 254 | analogData.buttons.bit0 = but5.getData(); |
| rvt | 5:a0bb17c379ce | 255 | analogData.buttons.bit1 = but6.getData(); |
| rvt | 5:a0bb17c379ce | 256 | analogData.buttons.bit2 = but7.getData(); |
| rvt | 5:a0bb17c379ce | 257 | analogData.buttons.bit3 = but8.getData(); |
| rvt | 5:a0bb17c379ce | 258 | analogData.buttons.bit4 = but9.getData(); |
| rvt | 5:a0bb17c379ce | 259 | analogData.buttons.bit5 = but10.getData(); |
| rvt | 5:a0bb17c379ce | 260 | analogData.buttons.bit6 = but11.getData(); |
| rvt | 5:a0bb17c379ce | 261 | analogData.buttons.bit7 = but12.getData(); |
| rvt | 5:a0bb17c379ce | 262 | analogData.buttons.bit8 = but13.getData(); |
| rvt | 5:a0bb17c379ce | 263 | analogData.buttons.bit9 = but14.getData(); |
| rvt | 5:a0bb17c379ce | 264 | analogData.buttons.bit10 = but15.getData(); |
| rvt | 5:a0bb17c379ce | 265 | analogData.buttons.bit11 = but16.getData(); |
| rvt | 5:a0bb17c379ce | 266 | analogData.buttons.bit12 = but17.getData(); |
| rvt | 5:a0bb17c379ce | 267 | analogData.buttons.bit13 = but21.getData(); |
| rvt | 5:a0bb17c379ce | 268 | analogData.buttons.bit14 = but22.getData(); |
| rvt | 5:a0bb17c379ce | 269 | analogData.buttons.bit15 = but23.getData(); |
| rvt | 5:a0bb17c379ce | 270 | analogData.buttons.bit16 = but24.getData(); |
| rvt | 5:a0bb17c379ce | 271 | analogData.buttons.bit17 = but25.getData(); |
| rvt | 4:2cc58c173de8 | 272 | |
| rvt | 2:ae7a31a3c618 | 273 | analogData.value1 = ai1.getData(); |
| rvt | 2:ae7a31a3c618 | 274 | analogData.value2 = ai2.getData(); |
| rvt | 5:a0bb17c379ce | 275 | analogData.value3 = ai3.getData(); |
| rvt | 4:2cc58c173de8 | 276 | analogData.value4 = 0.; |
| rvt | 0:33bc88c4ab31 | 277 | analogValueMutex.unlock(); |
| rvt | 0:33bc88c4ab31 | 278 | |
| rvt | 0:33bc88c4ab31 | 279 | // Signal that data has been changed |
| rvt | 0:33bc88c4ab31 | 280 | _hid_thread.signal_set(0x1); |
| rvt | 0:33bc88c4ab31 | 281 | } |
| rvt | 0:33bc88c4ab31 | 282 | Thread::wait(1); |
| rvt | 0:33bc88c4ab31 | 283 | } |
| rvt | 0:33bc88c4ab31 | 284 | } |
| rvt | 0:33bc88c4ab31 | 285 |
