ELEC351 SUBMISSION - Same as on the DLE

/media/uploads/Luka_Danilovic/elec_315_prototype_assembly.jpg

Committer:
Luka_Danilovic
Date:
Wed Jan 10 09:49:43 2018 +0000
Revision:
0:c66224a27cf8
ELEC351 SUBMISSION - SAme as on the DLE

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Luka_Danilovic 0:c66224a27cf8 1 #include "main.hpp" // Contains other HPP files and function prototypes (WITH INCLUSION SAFEGUARDS)
Luka_Danilovic 0:c66224a27cf8 2
Luka_Danilovic 0:c66224a27cf8 3
Luka_Danilovic 0:c66224a27cf8 4 //**********************************SETUP*************************************//
Luka_Danilovic 0:c66224a27cf8 5 C_sensorData sensorData; // Initialise samplingMaster
Luka_Danilovic 0:c66224a27cf8 6 C_DT Date_Time; // Initialise loggingMaster
Luka_Danilovic 0:c66224a27cf8 7 circularBuffer buffer; // Initialise recordsMaster
Luka_Danilovic 0:c66224a27cf8 8 TextLCD LCD(D9, D8, D7, D6, D4, D2); // Initialise LCD driver
Luka_Danilovic 0:c66224a27cf8 9 SDBlockDevice sd(PB_5, D12, D13, D10); // Initialise SD card driver
Luka_Danilovic 0:c66224a27cf8 10 Thread wdtThread(osPriorityNormal); // Crate thread for WDT
Luka_Danilovic 0:c66224a27cf8 11 Thread samplingThread (osPriorityRealtime); // Create thread for sampling
Luka_Danilovic 0:c66224a27cf8 12 Thread puttyThread (osPriorityNormal); // Create thread for PuTTY
Luka_Danilovic 0:c66224a27cf8 13 Thread displayThread (osPriorityNormal); // Create thread for LCD
Luka_Danilovic 0:c66224a27cf8 14 Thread htmlThread (osPriorityNormal, OS_STACK_SIZE*2); // Create thread for HTML
Luka_Danilovic 0:c66224a27cf8 15 Thread sdcardThread (osPriorityNormal,OS_STACK_SIZE*2); // Create thread for sd card
Luka_Danilovic 0:c66224a27cf8 16 Ticker samplingTick; // Create ticker for sampling
Luka_Danilovic 0:c66224a27cf8 17 TDS_record tempRec; // Create temporary storage (GLOBAL)
Luka_Danilovic 0:c66224a27cf8 18 EthernetInterface eth; // Create eth interface (GLOBAL)
Luka_Danilovic 0:c66224a27cf8 19 float interval; // Create sampling interval (GLOBAL)
Luka_Danilovic 0:c66224a27cf8 20 bool wdtFlag; // Create wdt flag (GLOBAL)
Luka_Danilovic 0:c66224a27cf8 21 bool state; // Create sampling state (GLOBAL)
Luka_Danilovic 0:c66224a27cf8 22 DigitalIn button(USER_BUTTON); // Create D_in for blue button
Luka_Danilovic 0:c66224a27cf8 23 DigitalOut greenLED(PB_11); // Create D_out for green LED
Luka_Danilovic 0:c66224a27cf8 24 DigitalOut sdSatusLED(PE_15); // Create D_out for white LED
Luka_Danilovic 0:c66224a27cf8 25
Luka_Danilovic 0:c66224a27cf8 26
Luka_Danilovic 0:c66224a27cf8 27 //********************************FUNCTIONS***********************************//
Luka_Danilovic 0:c66224a27cf8 28
Luka_Danilovic 0:c66224a27cf8 29 void samplingISR() // Sampling interupt
Luka_Danilovic 0:c66224a27cf8 30 {
Luka_Danilovic 0:c66224a27cf8 31 samplingThread.signal_set(GET_SAMPLE); // Signal thread to sample
Luka_Danilovic 0:c66224a27cf8 32 }
Luka_Danilovic 0:c66224a27cf8 33
Luka_Danilovic 0:c66224a27cf8 34
Luka_Danilovic 0:c66224a27cf8 35 void samplingFunction() // Reads sensors and time
Luka_Danilovic 0:c66224a27cf8 36 {
Luka_Danilovic 0:c66224a27cf8 37 while(true) {
Luka_Danilovic 0:c66224a27cf8 38
Luka_Danilovic 0:c66224a27cf8 39 Thread::signal_wait(GET_SAMPLE); // Wait on signal
Luka_Danilovic 0:c66224a27cf8 40 if (state == 1) { // If sampling on
Luka_Danilovic 0:c66224a27cf8 41 tempRec.record_Data = sensorData.read(); // Aquire sensors
Luka_Danilovic 0:c66224a27cf8 42 tempRec.record_DT = Date_Time.getDT(); // Aquire date and time
Luka_Danilovic 0:c66224a27cf8 43 buffer.write(tempRec); // Record sample
Luka_Danilovic 0:c66224a27cf8 44 Thread::signal_clr(GET_SAMPLE); // Clear signal
Luka_Danilovic 0:c66224a27cf8 45
Luka_Danilovic 0:c66224a27cf8 46 // Branch out when sampling is finished
Luka_Danilovic 0:c66224a27cf8 47 displayThread.signal_set(DISP_SAMPLE); // Signal thread to display sample
Luka_Danilovic 0:c66224a27cf8 48 htmlThread.signal_set(HTML_SAMPLE); // Signal thread to update webpage
Luka_Danilovic 0:c66224a27cf8 49 sdcardThread.signal_set(SD_SAMPLE); // Signal thread to write to sd card
Luka_Danilovic 0:c66224a27cf8 50 }
Luka_Danilovic 0:c66224a27cf8 51
Luka_Danilovic 0:c66224a27cf8 52 // WDT flag set
Luka_Danilovic 0:c66224a27cf8 53 wdtFlag = 1;
Luka_Danilovic 0:c66224a27cf8 54
Luka_Danilovic 0:c66224a27cf8 55 }
Luka_Danilovic 0:c66224a27cf8 56 }
Luka_Danilovic 0:c66224a27cf8 57
Luka_Danilovic 0:c66224a27cf8 58
Luka_Danilovic 0:c66224a27cf8 59 void wdtFunction() /* Simplest Watch Dog Timer that resets the system
Luka_Danilovic 0:c66224a27cf8 60 if the sampling thread does not set a flag every
Luka_Danilovic 0:c66224a27cf8 61 sampling interval */
Luka_Danilovic 0:c66224a27cf8 62 {
Luka_Danilovic 0:c66224a27cf8 63 while(true) { // Always perform check
Luka_Danilovic 0:c66224a27cf8 64 Thread::wait(interval*1000+5); // Block (For lower plwer consumption) until ready to read
Luka_Danilovic 0:c66224a27cf8 65 if (wdtFlag == 0) { // If flag not set
Luka_Danilovic 0:c66224a27cf8 66 NVIC_SystemReset();; // Reset chip
Luka_Danilovic 0:c66224a27cf8 67 } else { // else
Luka_Danilovic 0:c66224a27cf8 68 wdtFlag =0; // clear flag and loop
Luka_Danilovic 0:c66224a27cf8 69 }
Luka_Danilovic 0:c66224a27cf8 70 }
Luka_Danilovic 0:c66224a27cf8 71 }
Luka_Danilovic 0:c66224a27cf8 72
Luka_Danilovic 0:c66224a27cf8 73 void displayFunction() // Displays sample to LCD & PuTTY
Luka_Danilovic 0:c66224a27cf8 74 {
Luka_Danilovic 0:c66224a27cf8 75 while(true) {
Luka_Danilovic 0:c66224a27cf8 76 Thread::signal_wait(DISP_SAMPLE); // Wait on signal
Luka_Danilovic 0:c66224a27cf8 77 buffer.read(0); // Read last record and diplay
Luka_Danilovic 0:c66224a27cf8 78 LCD.cls(); // Clear LCD
Luka_Danilovic 0:c66224a27cf8 79 LCD.printf("Temp Press Light%.1f %4.0f %.3f", tempRec.record_Data.temp, tempRec.record_Data.pres, tempRec.record_Data.ligt);
Luka_Danilovic 0:c66224a27cf8 80 Thread::signal_clr(DISP_SAMPLE); // Clear signal
Luka_Danilovic 0:c66224a27cf8 81 }
Luka_Danilovic 0:c66224a27cf8 82 }
Luka_Danilovic 0:c66224a27cf8 83
Luka_Danilovic 0:c66224a27cf8 84
Luka_Danilovic 0:c66224a27cf8 85 void sdFunction() // Writes data to sd card
Luka_Danilovic 0:c66224a27cf8 86 {
Luka_Danilovic 0:c66224a27cf8 87 Thread::wait(10); // Powerup time
Luka_Danilovic 0:c66224a27cf8 88 greenLED = 1; // Turn off at the start
Luka_Danilovic 0:c66224a27cf8 89 FILE* fp; // File pointer
Luka_Danilovic 0:c66224a27cf8 90 int batchCount; // Record grouping counter
Luka_Danilovic 0:c66224a27cf8 91 batchCount = 0; // Reset
Luka_Danilovic 0:c66224a27cf8 92
Luka_Danilovic 0:c66224a27cf8 93 if ( sd.init() == 0) { // Check for initialisation
Luka_Danilovic 0:c66224a27cf8 94 FATFileSystem fs("sd", &sd); // Create file system
Luka_Danilovic 0:c66224a27cf8 95
Luka_Danilovic 0:c66224a27cf8 96 fp = fopen("/sd/records.txt","a"); // Open file for write
Luka_Danilovic 0:c66224a27cf8 97
Luka_Danilovic 0:c66224a27cf8 98 if (fp != NULL) { // Check for sucess
Luka_Danilovic 0:c66224a27cf8 99
Luka_Danilovic 0:c66224a27cf8 100 sdSatusLED = 0; // Turn on status LED (white)
Luka_Danilovic 0:c66224a27cf8 101
Luka_Danilovic 0:c66224a27cf8 102 while(button ==0) { // If dismounting
Luka_Danilovic 0:c66224a27cf8 103
Luka_Danilovic 0:c66224a27cf8 104 batchCount++;
Luka_Danilovic 0:c66224a27cf8 105 Thread::signal_wait(SD_SAMPLE); // Wait on signal
Luka_Danilovic 0:c66224a27cf8 106
Luka_Danilovic 0:c66224a27cf8 107 TDS_record sdRec = tempRec; // Copy for safe storage
Luka_Danilovic 0:c66224a27cf8 108
Luka_Danilovic 0:c66224a27cf8 109 // Write to file
Luka_Danilovic 0:c66224a27cf8 110 fprintf(fp, "\r Record number: %d \n\r", buffer.recNum()-1);
Luka_Danilovic 0:c66224a27cf8 111 fprintf(fp, " Date: %d.%d.%d \n\r", sdRec.record_DT.day, sdRec.record_DT.mnt, sdRec.record_DT.yr);
Luka_Danilovic 0:c66224a27cf8 112 fprintf(fp, " Time: %d:%d:%d \n\r", sdRec.record_DT.hr, sdRec.record_DT.min, sdRec.record_DT.sec);
Luka_Danilovic 0:c66224a27cf8 113 fprintf(fp, " Temperature = %1.2f \n\r", sdRec.record_Data.temp);
Luka_Danilovic 0:c66224a27cf8 114 fprintf(fp, " Pressure = %4.0f \n\r", sdRec.record_Data.pres);
Luka_Danilovic 0:c66224a27cf8 115 fprintf(fp, " Light Level = %1.3f \n\r", sdRec.record_Data.ligt);
Luka_Danilovic 0:c66224a27cf8 116 fprintf(fp, " \n\r \n\n");
Luka_Danilovic 0:c66224a27cf8 117
Luka_Danilovic 0:c66224a27cf8 118 if (batchCount == 10) { // Save in batches of 10
Luka_Danilovic 0:c66224a27cf8 119 batchCount =0; // Reset counter
Luka_Danilovic 0:c66224a27cf8 120 fclose(fp); // Save & Close file
Luka_Danilovic 0:c66224a27cf8 121 fp = fopen("/sd/records.txt","a"); // Open file for write
Luka_Danilovic 0:c66224a27cf8 122 }
Luka_Danilovic 0:c66224a27cf8 123
Luka_Danilovic 0:c66224a27cf8 124
Luka_Danilovic 0:c66224a27cf8 125 Thread::signal_clr(SD_SAMPLE); // Clear signal
Luka_Danilovic 0:c66224a27cf8 126
Luka_Danilovic 0:c66224a27cf8 127 }
Luka_Danilovic 0:c66224a27cf8 128
Luka_Danilovic 0:c66224a27cf8 129 sdSatusLED = 1; // Turn off status LED (white)
Luka_Danilovic 0:c66224a27cf8 130 fclose(fp); // Save & Close file
Luka_Danilovic 0:c66224a27cf8 131 sd.deinit(); // Uninitialise sd card
Luka_Danilovic 0:c66224a27cf8 132 for (int i=0; i <5; i++) { // Do 5 times
Luka_Danilovic 0:c66224a27cf8 133 greenLED = 0;
Luka_Danilovic 0:c66224a27cf8 134 wait(0.25); // Flash LED at 4Hz
Luka_Danilovic 0:c66224a27cf8 135 greenLED = 1;
Luka_Danilovic 0:c66224a27cf8 136 wait(0.25);
Luka_Danilovic 0:c66224a27cf8 137
Luka_Danilovic 0:c66224a27cf8 138 }
Luka_Danilovic 0:c66224a27cf8 139 }
Luka_Danilovic 0:c66224a27cf8 140 }
Luka_Danilovic 0:c66224a27cf8 141 }
Luka_Danilovic 0:c66224a27cf8 142
Luka_Danilovic 0:c66224a27cf8 143
Luka_Danilovic 0:c66224a27cf8 144
Luka_Danilovic 0:c66224a27cf8 145
Luka_Danilovic 0:c66224a27cf8 146 void htmlFunction() /* Creates and updates webpage. The code for setting up the
Luka_Danilovic 0:c66224a27cf8 147 ethernet conection & server is from our lab tasks:
Luka_Danilovic 0:c66224a27cf8 148 "https://os.mbed.com/teams/University-of-Plymouth-Stage-2-and-3/code/Task671-mbedos-FZ429-TCP-dynamic/file/76bd6f78cabc/main.cpp/" */
Luka_Danilovic 0:c66224a27cf8 149 {
Luka_Danilovic 0:c66224a27cf8 150 TDS_record htmlRec; /* Temporary storage that can be overwritten only
Luka_Danilovic 0:c66224a27cf8 151 when webpage is refreshed */
Luka_Danilovic 0:c66224a27cf8 152
Luka_Danilovic 0:c66224a27cf8 153 // Config ethernet connection
Luka_Danilovic 0:c66224a27cf8 154 eth.set_network(IP, NETMASK, GATEWAY);
Luka_Danilovic 0:c66224a27cf8 155 eth.connect();
Luka_Danilovic 0:c66224a27cf8 156
Luka_Danilovic 0:c66224a27cf8 157 TCPServer srv; //TCP IP Server
Luka_Danilovic 0:c66224a27cf8 158 TCPSocket clt_sock; //Socket for communication
Luka_Danilovic 0:c66224a27cf8 159 SocketAddress clt_addr; //Address of incoming connection
Luka_Danilovic 0:c66224a27cf8 160
Luka_Danilovic 0:c66224a27cf8 161 /* Open the server on ethernet stack */
Luka_Danilovic 0:c66224a27cf8 162 srv.open(&eth);
Luka_Danilovic 0:c66224a27cf8 163
Luka_Danilovic 0:c66224a27cf8 164 /* Bind the HTTP port (TCP 80) to the server */
Luka_Danilovic 0:c66224a27cf8 165 srv.bind(eth.get_ip_address(), 80);
Luka_Danilovic 0:c66224a27cf8 166
Luka_Danilovic 0:c66224a27cf8 167 /* Can handle 5 simultaneous connections */
Luka_Danilovic 0:c66224a27cf8 168 srv.listen(5);
Luka_Danilovic 0:c66224a27cf8 169
Luka_Danilovic 0:c66224a27cf8 170 while(true) {
Luka_Danilovic 0:c66224a27cf8 171
Luka_Danilovic 0:c66224a27cf8 172 using namespace std;
Luka_Danilovic 0:c66224a27cf8 173 //Block and wait on an incoming connection
Luka_Danilovic 0:c66224a27cf8 174 srv.accept(&clt_sock, &clt_addr);
Luka_Danilovic 0:c66224a27cf8 175
Luka_Danilovic 0:c66224a27cf8 176 htmlRec = tempRec; // Copy to safe storage for generating the html
Luka_Danilovic 0:c66224a27cf8 177
Luka_Danilovic 0:c66224a27cf8 178 // Strings to store record parameters
Luka_Danilovic 0:c66224a27cf8 179 // Record number
Luka_Danilovic 0:c66224a27cf8 180 char Num[32];
Luka_Danilovic 0:c66224a27cf8 181 // Date
Luka_Danilovic 0:c66224a27cf8 182 char Date[32];
Luka_Danilovic 0:c66224a27cf8 183 // Time
Luka_Danilovic 0:c66224a27cf8 184 char Time[32];
Luka_Danilovic 0:c66224a27cf8 185 // Sensor data
Luka_Danilovic 0:c66224a27cf8 186 char Temp[32];
Luka_Danilovic 0:c66224a27cf8 187 char Pres[32];
Luka_Danilovic 0:c66224a27cf8 188 char Ligt[32];
Luka_Danilovic 0:c66224a27cf8 189
Luka_Danilovic 0:c66224a27cf8 190
Luka_Danilovic 0:c66224a27cf8 191 //Convert to a C String
Luka_Danilovic 0:c66224a27cf8 192 // Record number
Luka_Danilovic 0:c66224a27cf8 193 sprintf(Num, " <h1> Record Number: %d </h1> \n\r", buffer.recNum());
Luka_Danilovic 0:c66224a27cf8 194 // Date
Luka_Danilovic 0:c66224a27cf8 195 sprintf(Date, "<p> Date: %d.%d.%d </p> \n\r", htmlRec.record_DT.day, htmlRec.record_DT.mnt, htmlRec.record_DT.yr);
Luka_Danilovic 0:c66224a27cf8 196 // Time
Luka_Danilovic 0:c66224a27cf8 197 sprintf(Time, "<p> Time: %d:%d:%d </p> \n\n\r", htmlRec.record_DT.hr, htmlRec.record_DT.min, htmlRec.record_DT.sec);
Luka_Danilovic 0:c66224a27cf8 198 // Sensor Data
Luka_Danilovic 0:c66224a27cf8 199 sprintf(Temp, "<p> Temperature = %1.2f </p> \n\r", htmlRec.record_Data.temp);
Luka_Danilovic 0:c66224a27cf8 200 sprintf(Pres, "<p> Pressure = %4.0f </p> \n\r", htmlRec.record_Data.pres);
Luka_Danilovic 0:c66224a27cf8 201 sprintf(Ligt, "<p> Light Level = %1.3f </p> \n\r", htmlRec.record_Data.ligt);
Luka_Danilovic 0:c66224a27cf8 202
Luka_Danilovic 0:c66224a27cf8 203 //Uses a C++ string to make it easier to concatenate
Luka_Danilovic 0:c66224a27cf8 204 string webpage;
Luka_Danilovic 0:c66224a27cf8 205
Luka_Danilovic 0:c66224a27cf8 206 //Build the C++ string webpage
Luka_Danilovic 0:c66224a27cf8 207 webpage = HTTP_HEADER; // Header
Luka_Danilovic 0:c66224a27cf8 208 webpage += Num; // Record number
Luka_Danilovic 0:c66224a27cf8 209 webpage += Date; // Record date
Luka_Danilovic 0:c66224a27cf8 210 webpage += Time; // Record time
Luka_Danilovic 0:c66224a27cf8 211 webpage += Temp; // Record temperature
Luka_Danilovic 0:c66224a27cf8 212 webpage += Pres; // Record preassure
Luka_Danilovic 0:c66224a27cf8 213 webpage += Ligt; // Record light level
Luka_Danilovic 0:c66224a27cf8 214 webpage += HTTP_FOOTER; // Webpage
Luka_Danilovic 0:c66224a27cf8 215
Luka_Danilovic 0:c66224a27cf8 216 //Send static HTML webpage (as a C string)
Luka_Danilovic 0:c66224a27cf8 217 clt_sock.send(webpage.c_str(), webpage.size()+8);
Luka_Danilovic 0:c66224a27cf8 218
Luka_Danilovic 0:c66224a27cf8 219 }
Luka_Danilovic 0:c66224a27cf8 220 }
Luka_Danilovic 0:c66224a27cf8 221
Luka_Danilovic 0:c66224a27cf8 222
Luka_Danilovic 0:c66224a27cf8 223 void changeSamplingT(float interval)
Luka_Danilovic 0:c66224a27cf8 224 {
Luka_Danilovic 0:c66224a27cf8 225 if (interval < (float)0.2) {
Luka_Danilovic 0:c66224a27cf8 226 interval = 0.2; // Max sampling interval 5Hz
Luka_Danilovic 0:c66224a27cf8 227 }
Luka_Danilovic 0:c66224a27cf8 228 samplingTick.detach();
Luka_Danilovic 0:c66224a27cf8 229 samplingTick.attach(&samplingISR, interval); //Dettach and Re-atttach ISR to ticker
Luka_Danilovic 0:c66224a27cf8 230 }
Luka_Danilovic 0:c66224a27cf8 231
Luka_Danilovic 0:c66224a27cf8 232
Luka_Danilovic 0:c66224a27cf8 233 //**********************************MAIN**************************************//
Luka_Danilovic 0:c66224a27cf8 234 int main()
Luka_Danilovic 0:c66224a27cf8 235 {
Luka_Danilovic 0:c66224a27cf8 236 state = 1; // Turn on sampling
Luka_Danilovic 0:c66224a27cf8 237 sdSatusLED = 1; // Turn sd card status LED off
Luka_Danilovic 0:c66224a27cf8 238 interval = 15; // Default sampling interval
Luka_Danilovic 0:c66224a27cf8 239 samplingTick.attach(&samplingISR, interval); // Attach ISR
Luka_Danilovic 0:c66224a27cf8 240 wdtThread.start(wdtFunction); // Start WTD thread
Luka_Danilovic 0:c66224a27cf8 241 samplingThread.start(samplingFunction); // Start sampling thread
Luka_Danilovic 0:c66224a27cf8 242 displayThread.start(displayFunction); // Start display thread
Luka_Danilovic 0:c66224a27cf8 243 htmlThread.start(htmlFunction); // Start HTML thread
Luka_Danilovic 0:c66224a27cf8 244 sdcardThread.start(sdFunction); // Start sd card thread
Luka_Danilovic 0:c66224a27cf8 245 Date_Time.setD(10, 10, 2018); // Set date to submission deadline
Luka_Danilovic 0:c66224a27cf8 246 Date_Time.setT(10, 00, 00); // Set time to submission deadline
Luka_Danilovic 0:c66224a27cf8 247 LCD.printf("Hello, Waiting on first sample"); // Show signs of life on LCD
Luka_Danilovic 0:c66224a27cf8 248
Luka_Danilovic 0:c66224a27cf8 249
Luka_Danilovic 0:c66224a27cf8 250 /*=========================== DEMO CODE ==================================*/
Luka_Danilovic 0:c66224a27cf8 251 // interval=1; // Set new interval to 1 sec
Luka_Danilovic 0:c66224a27cf8 252 // changeSamplingT(interval); // Change the interval
Luka_Danilovic 0:c66224a27cf8 253 // Thread::wait(3000); // Wait for 3 sample intervals
Luka_Danilovic 0:c66224a27cf8 254 // state = 0; // Turn off sampling
Luka_Danilovic 0:c66224a27cf8 255 // buffer.del(1); // Delete record number 1
Luka_Danilovic 0:c66224a27cf8 256 // buffer.read(1); // Read record number 1
Luka_Danilovic 0:c66224a27cf8 257 // Thread::wait(3000); // Wait so user can see
Luka_Danilovic 0:c66224a27cf8 258 // buffer.delAll(); // Delete all records
Luka_Danilovic 0:c66224a27cf8 259 // Thread::wait(3000); // Wait so user can see
Luka_Danilovic 0:c66224a27cf8 260 // buffer.readAll(); // Read all records
Luka_Danilovic 0:c66224a27cf8 261 /*======================= END OF DEMO CODE ===============================*/
Luka_Danilovic 0:c66224a27cf8 262
Luka_Danilovic 0:c66224a27cf8 263 Thread::wait(osWaitForever); /* Scheduler put thread to sleep
Luka_Danilovic 0:c66224a27cf8 264 untill interupt to sample occurs but
Luka_Danilovic 0:c66224a27cf8 265 function remains in scope */
Luka_Danilovic 0:c66224a27cf8 266 }
Luka_Danilovic 0:c66224a27cf8 267