Jon Freeman / Mbed 2 deprecated Alternator2020_06

Dependencies:   mbed BufferedSerial Servo2 PCT2075 I2CEeprom FastPWM

Committer:
JonFreeman
Date:
Mon Jun 08 13:46:52 2020 +0000
Revision:
2:8e7b51353f32
Parent:
1:450090bdb6f4
Child:
3:43cb067ecd00
About to revamp i2c

Who changed what in which revision?

UserRevisionLine numberNew contents of line
JonFreeman 0:77803b3ee157 1 /*
JonFreeman 0:77803b3ee157 2 Command Line Interpreter code module.
JonFreeman 0:77803b3ee157 3 Purpose -
JonFreeman 0:77803b3ee157 4 Provides easy interface to pc terminal programme for use during programme development, debug etc.
JonFreeman 0:77803b3ee157 5 Also usable as comms subsystem in finished code for accepting commands, reporting data etc.
JonFreeman 0:77803b3ee157 6 */
JonFreeman 0:77803b3ee157 7 #include "mbed.h"
JonFreeman 0:77803b3ee157 8 #include "Alternator.h"
JonFreeman 0:77803b3ee157 9 //#include "BufferedSerial.h"
JonFreeman 0:77803b3ee157 10 #include <cctype>
JonFreeman 0:77803b3ee157 11 using namespace std;
JonFreeman 0:77803b3ee157 12
JonFreeman 1:450090bdb6f4 13 extern eeprom_settings user_settings ;
JonFreeman 0:77803b3ee157 14 //eeprom_settings mode ;
JonFreeman 0:77803b3ee157 15
JonFreeman 1:450090bdb6f4 16 //extern int ver, vef, measured_pw_us;
JonFreeman 1:450090bdb6f4 17 extern void set_throttle_limit (struct parameters & a) ;
JonFreeman 2:8e7b51353f32 18 //extern void speed_control_factor_set (struct parameters & a) ;
JonFreeman 1:450090bdb6f4 19 extern void query_system (struct parameters & a) ;
JonFreeman 0:77803b3ee157 20 extern uint32_t ReadEngineRPM () ;
JonFreeman 0:77803b3ee157 21 extern double Read_BatteryVolts () ;
JonFreeman 2:8e7b51353f32 22 //extern void Read_Ammeter (double *) ;
JonFreeman 0:77803b3ee157 23
JonFreeman 0:77803b3ee157 24
JonFreeman 0:77803b3ee157 25
JonFreeman 0:77803b3ee157 26
JonFreeman 0:77803b3ee157 27
JonFreeman 0:77803b3ee157 28 // WithOUT RTOS
JonFreeman 0:77803b3ee157 29 //extern BufferedSerial pc;
JonFreeman 1:450090bdb6f4 30
JonFreeman 1:450090bdb6f4 31 #ifdef TARGET_NUCLEO_L432KC //
JonFreeman 0:77803b3ee157 32 extern Serial pc;
JonFreeman 1:450090bdb6f4 33 #else
JonFreeman 1:450090bdb6f4 34 extern BufferedSerial pc;
JonFreeman 1:450090bdb6f4 35 #endif
JonFreeman 1:450090bdb6f4 36 //extern double test_pot; // These used in knifeandfork code testing only
JonFreeman 2:8e7b51353f32 37 extern void maketable () ;
JonFreeman 2:8e7b51353f32 38
JonFreeman 2:8e7b51353f32 39 void table_tweak_cmd (struct parameters & a) { // Requires two params. First '20', '25' etc representing hundreds RPM. Second 0 to 99 percent
JonFreeman 2:8e7b51353f32 40 char txt[100];
JonFreeman 2:8e7b51353f32 41 uint32_t d[3];
JonFreeman 2:8e7b51353f32 42 txt[0] = 0;
JonFreeman 2:8e7b51353f32 43 if (a.numof_dbls != 2)
JonFreeman 2:8e7b51353f32 44 sprintf (txt, "Need 2 params, got %d, ", a.numof_dbls);
JonFreeman 2:8e7b51353f32 45 else {
JonFreeman 2:8e7b51353f32 46 d[2] = (uint32_t)a.dbl[0];
JonFreeman 2:8e7b51353f32 47 d[0] = d[2] / 5;
JonFreeman 2:8e7b51353f32 48 d[1] = (uint32_t)a.dbl[1];
JonFreeman 2:8e7b51353f32 49 if (d[0] > 16 || d[1] > 99 || d[2] != d[0] * 5)
JonFreeman 2:8e7b51353f32 50 sprintf (txt + strlen(txt), "Param out of range %d, %d, ", d[2], d[1]);
JonFreeman 2:8e7b51353f32 51 else {
JonFreeman 2:8e7b51353f32 52 pc.printf ("Off to reset table %d RPM, %d percent\r\n", d[2] * 100, d[1]);
JonFreeman 2:8e7b51353f32 53 user_settings.wr ((char)d[1], d[0]);
JonFreeman 2:8e7b51353f32 54 user_settings.save ();
JonFreeman 2:8e7b51353f32 55 maketable ();
JonFreeman 2:8e7b51353f32 56 }
JonFreeman 2:8e7b51353f32 57 }
JonFreeman 2:8e7b51353f32 58 if (txt[0])
JonFreeman 2:8e7b51353f32 59 pc.printf ("Errors in table_tweak_cmd - %s\r\n", txt);
JonFreeman 2:8e7b51353f32 60 else
JonFreeman 2:8e7b51353f32 61 pc.printf ("Good in table_tweak_cmd, RPM=%d, percentage=%d\r\n", d[0] * 500, d[1]);
JonFreeman 2:8e7b51353f32 62 }
JonFreeman 0:77803b3ee157 63
JonFreeman 0:77803b3ee157 64 //extern int numof_eeprom_options2 ;
JonFreeman 0:77803b3ee157 65 //extern struct optpar const option_list2[] ;
JonFreeman 0:77803b3ee157 66 extern struct optpar option_list2[] ;
JonFreeman 0:77803b3ee157 67
JonFreeman 0:77803b3ee157 68 /**void mode_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents
JonFreeman 0:77803b3ee157 69 * mode_cmd called only from pc comms. No sense calling from Touch Screen Controller
JonFreeman 0:77803b3ee157 70 *
JonFreeman 0:77803b3ee157 71 * Called without parameters - Lists to pc terminal current settings
JonFreeman 0:77803b3ee157 72 *
JonFreeman 0:77803b3ee157 73 */
JonFreeman 0:77803b3ee157 74 void mode19_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents
JonFreeman 0:77803b3ee157 75 {
JonFreeman 1:450090bdb6f4 76
JonFreeman 0:77803b3ee157 77 char temps[36];
JonFreeman 0:77803b3ee157 78 int i;
JonFreeman 0:77803b3ee157 79 pc.printf ("\r\nmode - Set system data in EEPROM - Jan 2019\r\nSyntax 'mode' with no parameters lists current state.\r\n");
JonFreeman 0:77803b3ee157 80 if (a.numof_dbls) { // If more than 0 parameters supplied
JonFreeman 0:77803b3ee157 81 for (i = 0; i < a.numof_dbls; i++)
JonFreeman 0:77803b3ee157 82 temps[i] = (char)a.dbl[i]; // recast doubles to char
JonFreeman 0:77803b3ee157 83 while (i < 33)
JonFreeman 0:77803b3ee157 84 temps[i++] = 0;
JonFreeman 0:77803b3ee157 85 i = (int)a.dbl[0];
JonFreeman 0:77803b3ee157 86 switch (i) {
JonFreeman 0:77803b3ee157 87 case 0: case 1: case 2: case 3: case 4:
JonFreeman 0:77803b3ee157 88 case 5: case 6: case 7: case 8:
JonFreeman 0:77803b3ee157 89 if (temps[1] >= option_list2[i].min && temps[1] <= option_list2[i].max)
JonFreeman 1:450090bdb6f4 90 user_settings.wr(temps[1], RPM0 + i);
JonFreeman 0:77803b3ee157 91 break;
JonFreeman 0:77803b3ee157 92 case 37: // set pwm scale factor
JonFreeman 0:77803b3ee157 93 if (temps[1] >= option_list2[PWM_SCALE].min && temps[1] <= option_list2[PWM_SCALE].max)
JonFreeman 1:450090bdb6f4 94 user_settings.wr(temps[1], PWM_SCALE);
JonFreeman 0:77803b3ee157 95 break;
JonFreeman 0:77803b3ee157 96 case 83: // set to defaults
JonFreeman 1:450090bdb6f4 97 user_settings.set_defaults ();
JonFreeman 0:77803b3ee157 98 break;
JonFreeman 0:77803b3ee157 99 case 9: // 9 Save settings
JonFreeman 1:450090bdb6f4 100 user_settings.save ();
JonFreeman 0:77803b3ee157 101 pc.printf ("Saving settings to EEPROM\r\n");
JonFreeman 0:77803b3ee157 102 break;
JonFreeman 0:77803b3ee157 103 default:
JonFreeman 0:77803b3ee157 104 break;
JonFreeman 0:77803b3ee157 105 } // endof switch
JonFreeman 0:77803b3ee157 106 } // endof // If more than 0 parameters supplied
JonFreeman 0:77803b3ee157 107 else {
JonFreeman 0:77803b3ee157 108 pc.printf ("No Changes\r\n");
JonFreeman 0:77803b3ee157 109 }
JonFreeman 1:450090bdb6f4 110 pc.printf ("mode 0\t%s, [%d]\r\n", option_list2[0].t, user_settings.rd(RPM0));
JonFreeman 1:450090bdb6f4 111 pc.printf ("mode 1\t%s, [%d]\r\n", option_list2[1].t, user_settings.rd(RPM1));
JonFreeman 1:450090bdb6f4 112 pc.printf ("mode 2\t%s, [%d]\r\n", option_list2[2].t, user_settings.rd(RPM2));
JonFreeman 1:450090bdb6f4 113 pc.printf ("mode 3\t%s, [%d]\r\n", option_list2[3].t, user_settings.rd(RPM3));
JonFreeman 1:450090bdb6f4 114 pc.printf ("mode 4\t%s, [%d]\r\n", option_list2[4].t, user_settings.rd(RPM4));
JonFreeman 1:450090bdb6f4 115 pc.printf ("mode 5\t%s, [%d]\r\n", option_list2[5].t, user_settings.rd(RPM5));
JonFreeman 1:450090bdb6f4 116 pc.printf ("mode 6\t%s, [%d]\r\n", option_list2[6].t, user_settings.rd(RPM6));
JonFreeman 1:450090bdb6f4 117 pc.printf ("mode 7\t%s, [%d]\r\n", option_list2[7].t, user_settings.rd(RPM7));
JonFreeman 1:450090bdb6f4 118 pc.printf ("mode 8\t%s, [%d]\r\n", option_list2[8].t, user_settings.rd(RPM8));
JonFreeman 0:77803b3ee157 119
JonFreeman 1:450090bdb6f4 120 pc.printf ("mode 37\t%s, [%d]\r\n", option_list2[PWM_SCALE].t, user_settings.rd(PWM_SCALE));
JonFreeman 0:77803b3ee157 121 pc.printf ("mode 83\tSet to defaults\r\n");
JonFreeman 0:77803b3ee157 122 pc.printf ("mode 9\tSave settings\r\r\n");
JonFreeman 0:77803b3ee157 123
JonFreeman 0:77803b3ee157 124 }
JonFreeman 0:77803b3ee157 125
JonFreeman 2:8e7b51353f32 126 /*void gpcmd (struct parameters & a) {
JonFreeman 1:450090bdb6f4 127 pc.printf ("pwm=%.3f\r\n", user_settings.get_pwm ((int)a.dbl[0]));
JonFreeman 2:8e7b51353f32 128 }*/
JonFreeman 0:77803b3ee157 129
JonFreeman 1:450090bdb6f4 130 extern VEXT_Data Field;
JonFreeman 1:450090bdb6f4 131
JonFreeman 2:8e7b51353f32 132 void rfcmd (struct parameters & a) { // Note casts all wrong here, values are 64 bit
JonFreeman 2:8e7b51353f32 133 pc.printf ("Field.measured_period = %u", (uint32_t)Field.measured_period);
JonFreeman 2:8e7b51353f32 134 pc.printf (", Field.measured_pw_us = %u, duty_cycle = %.3f\r\n", (uint32_t)Field.measured_pw_us, Field.duty_cycle());
JonFreeman 0:77803b3ee157 135 }
JonFreeman 0:77803b3ee157 136
JonFreeman 2:8e7b51353f32 137 //extern void set_RPM_demand (uint32_t d) ;
JonFreeman 0:77803b3ee157 138
JonFreeman 2:8e7b51353f32 139 /*void set_rpm_cmd (struct parameters & a) {
JonFreeman 0:77803b3ee157 140 pc.printf ("setting RPM to %d\r\n",(int)a.dbl[0]);
JonFreeman 0:77803b3ee157 141 set_RPM_demand ((uint32_t)a.dbl[0]);
JonFreeman 2:8e7b51353f32 142 }*/
JonFreeman 0:77803b3ee157 143
JonFreeman 2:8e7b51353f32 144 /*void speedcmd (struct parameters & a) {
JonFreeman 0:77803b3ee157 145 int s = ReadEngineRPM ();
JonFreeman 1:450090bdb6f4 146 pc.printf ("speed %d, pwm %.3f\r\n", s, user_settings.get_pwm(s));
JonFreeman 2:8e7b51353f32 147 }*/
JonFreeman 0:77803b3ee157 148
JonFreeman 0:77803b3ee157 149 void vcmd (struct parameters & a) {
JonFreeman 0:77803b3ee157 150 pc.printf ("volts %.2f\r\n", Read_BatteryVolts());
JonFreeman 0:77803b3ee157 151 }
JonFreeman 0:77803b3ee157 152
JonFreeman 2:8e7b51353f32 153 /*void icmd (struct parameters & a) {
JonFreeman 1:450090bdb6f4 154 double results[4];
JonFreeman 1:450090bdb6f4 155 //double * ampsptr =
JonFreeman 1:450090bdb6f4 156 Read_Ammeter(results) ;
JonFreeman 1:450090bdb6f4 157 pc.printf ("amps %.3f, offset %.3f\r\n", results[0], results[1]);
JonFreeman 2:8e7b51353f32 158 }*/
JonFreeman 1:450090bdb6f4 159
JonFreeman 0:77803b3ee157 160 extern void set_servo (double p) ; // Only for test, called from cli
JonFreeman 0:77803b3ee157 161
JonFreeman 0:77803b3ee157 162 void set_servo_cmd (struct parameters & a) {
JonFreeman 0:77803b3ee157 163 double p = a.dbl[0] / 100.0;
JonFreeman 0:77803b3ee157 164 pc.printf ("servo %.2f\r\n", p);
JonFreeman 0:77803b3ee157 165 set_servo (p);
JonFreeman 0:77803b3ee157 166 }
JonFreeman 0:77803b3ee157 167
JonFreeman 1:450090bdb6f4 168 extern bool set_pwm (double) ; // Range 0.0 to 1.0
JonFreeman 1:450090bdb6f4 169 void p_cmd (struct parameters & a) {
JonFreeman 1:450090bdb6f4 170 // int32_t i = (int32_t)a.dbl[0];
JonFreeman 1:450090bdb6f4 171 pc.printf ("Setting PWM to %d percent\r\n", (int)(a.dbl[0] * 100.0));
JonFreeman 1:450090bdb6f4 172 set_pwm (a.dbl[0]);
JonFreeman 1:450090bdb6f4 173 }
JonFreeman 1:450090bdb6f4 174
JonFreeman 0:77803b3ee157 175 void null_cmd (struct parameters & a) {
JonFreeman 0:77803b3ee157 176 pc.printf ("At null_cmd, parameters : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
JonFreeman 0:77803b3ee157 177 }
JonFreeman 0:77803b3ee157 178
JonFreeman 0:77803b3ee157 179 void menucmd (struct parameters & a);
JonFreeman 0:77803b3ee157 180
JonFreeman 0:77803b3ee157 181 struct kb_command {
JonFreeman 0:77803b3ee157 182 const char * cmd_word; // points to text e.g. "menu"
JonFreeman 0:77803b3ee157 183 const char * explan;
JonFreeman 0:77803b3ee157 184 void (*f)(struct parameters &); // points to function
JonFreeman 0:77803b3ee157 185 } ;
JonFreeman 0:77803b3ee157 186
JonFreeman 0:77803b3ee157 187 struct kb_command const command_list[] = {
JonFreeman 0:77803b3ee157 188 {"?", "Lists available commands, same as ls", menucmd},
JonFreeman 2:8e7b51353f32 189 {"tt", "Table Tweak - 2 params RPM/100, percentage 0-99", table_tweak_cmd},
JonFreeman 0:77803b3ee157 190 {"rf", "Check rise and fall on VEXT", rfcmd},
JonFreeman 2:8e7b51353f32 191 // {"s", "Speed, RPM", speedcmd},
JonFreeman 0:77803b3ee157 192 {"v", "Read Battery volts", vcmd},
JonFreeman 2:8e7b51353f32 193 // {"i", "Read Ammeter", icmd},
JonFreeman 1:450090bdb6f4 194 {"p", "Set PWM 0 to 2400???", p_cmd},
JonFreeman 1:450090bdb6f4 195 {"q", "Query system - toggle message stream on/off", query_system},
JonFreeman 2:8e7b51353f32 196 // {"gp","Get pwm from RPM", gpcmd},
JonFreeman 0:77803b3ee157 197 {"mode", "See or set eeprom values", mode19_cmd},
JonFreeman 0:77803b3ee157 198 {"nu", "do nothing", null_cmd},
JonFreeman 1:450090bdb6f4 199 #ifndef SPEED_CONTROL_ENABLE // Includes engine revs servo control loop
JonFreeman 0:77803b3ee157 200 {"ser","set throttle servo direct 0 - 99", set_servo_cmd},
JonFreeman 1:450090bdb6f4 201 #endif
JonFreeman 2:8e7b51353f32 202 // {"sf","set speed control factor", speed_control_factor_set},
JonFreeman 2:8e7b51353f32 203 // {"sv","set engine RPM demand 2500 - 6000", set_rpm_cmd},
JonFreeman 1:450090bdb6f4 204 {"tl","set throttle_limit 0.0-1.0", set_throttle_limit},
JonFreeman 0:77803b3ee157 205 };
JonFreeman 0:77803b3ee157 206
JonFreeman 0:77803b3ee157 207 const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
JonFreeman 0:77803b3ee157 208 void menucmd (struct parameters & a)
JonFreeman 0:77803b3ee157 209 {
JonFreeman 1:450090bdb6f4 210 pc.printf("\r\nIntelligent Alternator Controller - Jon Freeman 2020\r\nAt menucmd function - listing commands:-\r\n");
JonFreeman 0:77803b3ee157 211 for(int i = 0; i < numof_menu_items; i++)
JonFreeman 0:77803b3ee157 212 pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
JonFreeman 0:77803b3ee157 213 pc.printf("End of List of Commands\r\n");
JonFreeman 0:77803b3ee157 214 }
JonFreeman 0:77803b3ee157 215
JonFreeman 0:77803b3ee157 216 void command_line_interpreter ()
JonFreeman 0:77803b3ee157 217 {
JonFreeman 1:450090bdb6f4 218
JonFreeman 0:77803b3ee157 219 const int MAX_CMD_LEN = 120;
JonFreeman 0:77803b3ee157 220 static char cmd_line[MAX_CMD_LEN + 4];
JonFreeman 0:77803b3ee157 221 static int cl_index = 0;
JonFreeman 0:77803b3ee157 222 int ch;
JonFreeman 0:77803b3ee157 223 char * pEnd;
JonFreeman 0:77803b3ee157 224 static struct parameters param_block ;
JonFreeman 0:77803b3ee157 225 while (pc.readable()) {
JonFreeman 0:77803b3ee157 226 ch = tolower(pc.getc());
JonFreeman 0:77803b3ee157 227 // pc.printf("%c", ch);
JonFreeman 0:77803b3ee157 228 if (cl_index > MAX_CMD_LEN) { // trap out stupidly long command lines
JonFreeman 0:77803b3ee157 229 pc.printf ("Error!! Stupidly long cmd line\r\n");
JonFreeman 0:77803b3ee157 230 cl_index = 0;
JonFreeman 0:77803b3ee157 231 }
JonFreeman 0:77803b3ee157 232 if (ch == '\r' || ch >= ' ' && ch <= 'z')
JonFreeman 0:77803b3ee157 233 pc.printf("%c", ch);
JonFreeman 0:77803b3ee157 234 else { // Using <Ctrl>+ 'F', 'B' for Y, 'L', 'R' for X, 'U', 'D' for Z
JonFreeman 0:77803b3ee157 235 cl_index = 0; // 6 2 12 18 21 4
JonFreeman 0:77803b3ee157 236 pc.printf("[%d]", ch);
JonFreeman 0:77803b3ee157 237 //nudger (ch); // was used on cnc to nudge axes a tad
JonFreeman 0:77803b3ee157 238 }
JonFreeman 0:77803b3ee157 239 if(ch != '\r') // was this the 'Enter' key?
JonFreeman 0:77803b3ee157 240 cmd_line[cl_index++] = ch; // added char to command being assembled
JonFreeman 0:77803b3ee157 241 else { // key was CR, may or may not be command to lookup
JonFreeman 0:77803b3ee157 242 cmd_line[cl_index] = 0; // null terminate command string
JonFreeman 0:77803b3ee157 243 if(cl_index) { // If have got some chars to lookup
JonFreeman 0:77803b3ee157 244 int i, wrdlen;
JonFreeman 0:77803b3ee157 245 for (i = 0; i < numof_menu_items; i++) { // Look for input match in command list
JonFreeman 0:77803b3ee157 246 wrdlen = strlen(command_list[i].cmd_word);
JonFreeman 0:77803b3ee157 247 if(strncmp(command_list[i].cmd_word, cmd_line, wrdlen) == 0 && !isalpha(cmd_line[wrdlen])) { // If match found
JonFreeman 0:77803b3ee157 248 for (int k = 0; k < MAX_PARAMS; k++) {
JonFreeman 0:77803b3ee157 249 param_block.dbl[k] = 0.0;
JonFreeman 0:77803b3ee157 250 }
JonFreeman 0:77803b3ee157 251 param_block.position_in_list = i;
JonFreeman 0:77803b3ee157 252 param_block.last_time = clock ();
JonFreeman 0:77803b3ee157 253 param_block.numof_dbls = 0;
JonFreeman 0:77803b3ee157 254 pEnd = cmd_line + wrdlen;
JonFreeman 0:77803b3ee157 255 while (*pEnd) { // Assemble all numerics as doubles
JonFreeman 0:77803b3ee157 256 param_block.dbl[param_block.numof_dbls++] = strtod (pEnd, &pEnd);
JonFreeman 0:77803b3ee157 257 while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) {
JonFreeman 0:77803b3ee157 258 pEnd++;
JonFreeman 0:77803b3ee157 259 }
JonFreeman 0:77803b3ee157 260 }
JonFreeman 0:77803b3ee157 261 pc.printf ("\r\n");
JonFreeman 0:77803b3ee157 262 // for (int k = 0; k < param_block.numof_dbls; k++)
JonFreeman 0:77803b3ee157 263 // pc.printf ("Read %.3f\r\n", param_block.dbl[k]);
JonFreeman 0:77803b3ee157 264 param_block.times[i] = clock();
JonFreeman 0:77803b3ee157 265 command_list[i].f(param_block); // execute command
JonFreeman 0:77803b3ee157 266 i = numof_menu_items + 1; // to exit for loop
JonFreeman 0:77803b3ee157 267 } // end of match found
JonFreeman 0:77803b3ee157 268 } // End of for numof_menu_items
JonFreeman 0:77803b3ee157 269 if(i == numof_menu_items)
JonFreeman 0:77803b3ee157 270 pc.printf("No Match Found for CMD [%s]\r\n", cmd_line);
JonFreeman 0:77803b3ee157 271 } // End of If have got some chars to lookup
JonFreeman 0:77803b3ee157 272 pc.printf("\r\n>");
JonFreeman 0:77803b3ee157 273 cl_index = 0;
JonFreeman 0:77803b3ee157 274 } // End of else key was CR, may or may not be command to lookup
JonFreeman 0:77803b3ee157 275 } // End of while (pc.readable())
JonFreeman 1:450090bdb6f4 276
JonFreeman 0:77803b3ee157 277 }
JonFreeman 0:77803b3ee157 278
JonFreeman 0:77803b3ee157 279