UART Command Parser Time Manager Data Store for SD Card for stm32l476 [it's not Licensed as BSD/GPLx]

Dependencies:   mbed SDFileSystem

Committer:
Inscape_ao
Date:
Mon Jun 10 23:50:04 2019 +0000
Revision:
17:c2709a9c0a68
Parent:
13:7cda5bef6390
Child:
19:36072b9b79f3
add RSH command

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Inscape_ao 2:a694440145e9 1 /** --- Includes --- */
Inscape_ao 2:a694440145e9 2 #include "mbed.h"
Inscape_ao 2:a694440145e9 3 #include "TimeManager.h"
Inscape_ao 2:a694440145e9 4 #include "UartReceiver.h"
Inscape_ao 2:a694440145e9 5 #include "CommandParser.h"
Inscape_ao 2:a694440145e9 6 #include "global.h"
Inscape_ao 2:a694440145e9 7 #include "string.h"
Inscape_ao 2:a694440145e9 8
Inscape_ao 2:a694440145e9 9 static int sampleHanlder(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 10 static int softResetHanlder(CommandParser *pC, char *arg, int exarg);
Inscape_ao 2:a694440145e9 11 static int setTimeHanlder(CommandParser *pC, char *arg, int exarg);
Inscape_ao 2:a694440145e9 12 static int getTimeHanlder(CommandParser *pC, char *arg, int exarg);
Inscape_ao 5:a37e3a15444b 13 static int startSDStore(CommandParser *pC, char *arg, int exarg);
Inscape_ao 5:a37e3a15444b 14 static int endSDStore(CommandParser *pC, char *arg, int exarg);
Inscape_ao 5:a37e3a15444b 15 static int writeSDStore(CommandParser *pC, char *arg, int exarg);
Inscape_ao 2:a694440145e9 16
Inscape_ao 7:9ab8809f9693 17 static int configIDHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 18 static int configWriteHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 19 static int configReadHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 20
Inscape_ao 7:9ab8809f9693 21 static int startRunHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 22 static int stopReqHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 23 static int checkRunStatHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 24 static int setRepeatCountHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 25 static int setRepeatStrideHandler(CommandParser *pC, char *arg, int exarg);
Inscape_ao 17:c2709a9c0a68 26 static int setRepeatSingleShotMode(CommandParser *pC, char *arg, int exarg);
Inscape_ao 7:9ab8809f9693 27
Inscape_ao 2:a694440145e9 28 /* Event Hankder */
Inscape_ao 2:a694440145e9 29 CmdParseRule rules[] = {
Inscape_ao 7:9ab8809f9693 30 /* -- sample -- */
Inscape_ao 2:a694440145e9 31 {"CMD", sampleHanlder, 0},
Inscape_ao 7:9ab8809f9693 32 /* Control SD-Card(NOT-SDHC) */
Inscape_ao 5:a37e3a15444b 33 {"SDS", startSDStore, 0},
Inscape_ao 5:a37e3a15444b 34 {"SDE", endSDStore, 0},
Inscape_ao 5:a37e3a15444b 35 {"SDW", writeSDStore, 0}, /* for TEST */
Inscape_ao 7:9ab8809f9693 36 /* Get Timestamp */
Inscape_ao 2:a694440145e9 37 {"GTS", getTimeHanlder, 0},
Inscape_ao 7:9ab8809f9693 38 /* Set Timestamp */
Inscape_ao 2:a694440145e9 39 {"TYR", setTimeHanlder, TimeManager::SetTimeMethod::Year},
Inscape_ao 2:a694440145e9 40 {"TMO", setTimeHanlder, TimeManager::SetTimeMethod::Month},
Inscape_ao 2:a694440145e9 41 {"TDA", setTimeHanlder, TimeManager::SetTimeMethod::Day},
Inscape_ao 2:a694440145e9 42 {"THR", setTimeHanlder, TimeManager::SetTimeMethod::Hour},
Inscape_ao 2:a694440145e9 43 {"TMI", setTimeHanlder, TimeManager::SetTimeMethod::Min},
Inscape_ao 2:a694440145e9 44 {"TSE", setTimeHanlder, TimeManager::SetTimeMethod::Sec},
Inscape_ao 7:9ab8809f9693 45 /* Sensing Control */
Inscape_ao 7:9ab8809f9693 46 {"CRS", startRunHandler, 0}, /* Run(start) */
Inscape_ao 7:9ab8809f9693 47 {"CRE", stopReqHandler, 0}, /* Stop -- req */
Inscape_ao 7:9ab8809f9693 48 {"CPS", checkRunStatHandler, 0}, /* Check ReadyToRun */
Inscape_ao 7:9ab8809f9693 49 {"RRP", setRepeatCountHandler, 0}, /* set repeat setting => Count */
Inscape_ao 7:9ab8809f9693 50 {"RMC", setRepeatStrideHandler, 0}, /* set repeat setting => Stride */
Inscape_ao 17:c2709a9c0a68 51 {"RSH", setRepeatSingleShotMode, 0}, /* set repeat setting => SingleShot */
Inscape_ao 7:9ab8809f9693 52 /* Device Control */
Inscape_ao 7:9ab8809f9693 53 {"SRS", softResetHanlder, 0}, /* TODO: Software Reset */
Inscape_ao 7:9ab8809f9693 54 {"CID", configIDHandler, 0}, /* Config ID Access */
Inscape_ao 7:9ab8809f9693 55 {"CFW", configWriteHandler, 0}, /* Config Write */
Inscape_ao 7:9ab8809f9693 56 {"CFR", configReadHandler, 0}, /* Config Read */
Inscape_ao 2:a694440145e9 57 };
Inscape_ao 2:a694440145e9 58
Inscape_ao 2:a694440145e9 59 int getNumOfRules = sizeof(rules)/sizeof(CmdParseRule);
Inscape_ao 2:a694440145e9 60
Inscape_ao 7:9ab8809f9693 61 /****************************************************/
Inscape_ao 7:9ab8809f9693 62 /* Event Handlers (Device Control) */
Inscape_ao 7:9ab8809f9693 63 /****************************************************/
Inscape_ao 7:9ab8809f9693 64 static int startRunHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 65 {
Inscape_ao 7:9ab8809f9693 66 bool success;
Inscape_ao 7:9ab8809f9693 67 success = pDevRept->start();
Inscape_ao 7:9ab8809f9693 68 if (!success) {
Inscape_ao 7:9ab8809f9693 69 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 70 return 1;
Inscape_ao 7:9ab8809f9693 71 }
Inscape_ao 7:9ab8809f9693 72 pC->reply();
Inscape_ao 7:9ab8809f9693 73 return 0;
Inscape_ao 7:9ab8809f9693 74 }
Inscape_ao 7:9ab8809f9693 75
Inscape_ao 7:9ab8809f9693 76 static int stopReqHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 77 {
Inscape_ao 7:9ab8809f9693 78 bool success;
Inscape_ao 7:9ab8809f9693 79 success = pDevRept->stop();
Inscape_ao 7:9ab8809f9693 80 if (!success) {
Inscape_ao 7:9ab8809f9693 81 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 82 return 1;
Inscape_ao 7:9ab8809f9693 83 }
Inscape_ao 7:9ab8809f9693 84 pC->reply();
Inscape_ao 7:9ab8809f9693 85 return 0;
Inscape_ao 7:9ab8809f9693 86 }
Inscape_ao 7:9ab8809f9693 87
Inscape_ao 7:9ab8809f9693 88 static int checkRunStatHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 89 {
Inscape_ao 7:9ab8809f9693 90 bool success;
Inscape_ao 7:9ab8809f9693 91 success = pDevRept->readyToStart();
Inscape_ao 7:9ab8809f9693 92 if (!success) {
Inscape_ao 7:9ab8809f9693 93 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 94 return 1;
Inscape_ao 7:9ab8809f9693 95 }
Inscape_ao 7:9ab8809f9693 96 pC->reply();
Inscape_ao 7:9ab8809f9693 97 return 0;
Inscape_ao 7:9ab8809f9693 98 }
Inscape_ao 7:9ab8809f9693 99
Inscape_ao 7:9ab8809f9693 100 static int setRepeatCountHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 101 {
Inscape_ao 7:9ab8809f9693 102 bool success;
Inscape_ao 7:9ab8809f9693 103 int setvalue = atoi(arg);
Inscape_ao 7:9ab8809f9693 104 success = pDevRept->setRepeatCount(setvalue);
Inscape_ao 7:9ab8809f9693 105 if (!success) {
Inscape_ao 7:9ab8809f9693 106 pC->reply(false, 4);
Inscape_ao 7:9ab8809f9693 107 return 1;
Inscape_ao 7:9ab8809f9693 108 }
Inscape_ao 7:9ab8809f9693 109 pC->reply();
Inscape_ao 7:9ab8809f9693 110 return 0;
Inscape_ao 7:9ab8809f9693 111 }
Inscape_ao 7:9ab8809f9693 112 static int setRepeatStrideHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 113 {
Inscape_ao 7:9ab8809f9693 114 bool success;
Inscape_ao 7:9ab8809f9693 115 char msflag = arg[3];
Inscape_ao 7:9ab8809f9693 116 char valsrc[4] = {0};
Inscape_ao 7:9ab8809f9693 117 int setvalue;
Inscape_ao 7:9ab8809f9693 118
Inscape_ao 7:9ab8809f9693 119 /* this Argment is "xxxs" or "xxxm" */
Inscape_ao 7:9ab8809f9693 120 memcpy(valsrc, arg, 3);
Inscape_ao 7:9ab8809f9693 121 setvalue = atoi(valsrc);
Inscape_ao 7:9ab8809f9693 122
Inscape_ao 7:9ab8809f9693 123 if (msflag == 'm') {
Inscape_ao 7:9ab8809f9693 124 setvalue *= 60; /* setvalue x 60(min to sec)*/
Inscape_ao 7:9ab8809f9693 125 } else if (msflag == 's') {
Inscape_ao 7:9ab8809f9693 126 /* NOP setvalue = setvalue */
Inscape_ao 7:9ab8809f9693 127 } else {
Inscape_ao 7:9ab8809f9693 128 /* invalid format */
Inscape_ao 7:9ab8809f9693 129 pC->reply(false, 6);
Inscape_ao 7:9ab8809f9693 130 return 1;
Inscape_ao 7:9ab8809f9693 131 }
Inscape_ao 7:9ab8809f9693 132 success = pDevRept->setRepeatStride(setvalue);
Inscape_ao 7:9ab8809f9693 133 if (!success) {
Inscape_ao 7:9ab8809f9693 134 pC->reply(false, 5);
Inscape_ao 7:9ab8809f9693 135 return 1;
Inscape_ao 7:9ab8809f9693 136 }
Inscape_ao 7:9ab8809f9693 137 pC->reply();
Inscape_ao 7:9ab8809f9693 138 return 0;
Inscape_ao 7:9ab8809f9693 139 }
Inscape_ao 8:b18a8764ecae 140
Inscape_ao 17:c2709a9c0a68 141 static int setRepeatSingleShotMode(CommandParser *pC, char *arg, int exarg)
Inscape_ao 17:c2709a9c0a68 142 {
Inscape_ao 17:c2709a9c0a68 143 bool setMode = true;
Inscape_ao 17:c2709a9c0a68 144 bool success;
Inscape_ao 17:c2709a9c0a68 145 if (strcmp("SET1", arg) == 0) {
Inscape_ao 17:c2709a9c0a68 146 setMode = true;
Inscape_ao 17:c2709a9c0a68 147 } else if (strcmp("CLR0", arg) == 0) {
Inscape_ao 17:c2709a9c0a68 148 setMode = false;
Inscape_ao 17:c2709a9c0a68 149 } else {
Inscape_ao 17:c2709a9c0a68 150 /* INVALID COMMAND */
Inscape_ao 17:c2709a9c0a68 151 pC->reply(false, 7);
Inscape_ao 17:c2709a9c0a68 152 return 1;
Inscape_ao 17:c2709a9c0a68 153 }
Inscape_ao 17:c2709a9c0a68 154 success = pDevRept->setRepeatSingleShot(setMode);
Inscape_ao 17:c2709a9c0a68 155 if (success != true) {
Inscape_ao 17:c2709a9c0a68 156 /* INVALID COMMAND in SetMode */
Inscape_ao 17:c2709a9c0a68 157 pC->reply(false, 5);
Inscape_ao 17:c2709a9c0a68 158 return 1;
Inscape_ao 17:c2709a9c0a68 159 }
Inscape_ao 17:c2709a9c0a68 160 pC->reply();
Inscape_ao 17:c2709a9c0a68 161 return 0;
Inscape_ao 17:c2709a9c0a68 162 }
Inscape_ao 17:c2709a9c0a68 163
Inscape_ao 7:9ab8809f9693 164 /****************************************************/
Inscape_ao 7:9ab8809f9693 165 /* Event Handlers (Device Control) */
Inscape_ao 7:9ab8809f9693 166 /****************************************************/
Inscape_ao 7:9ab8809f9693 167 static int configIDHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 168 {
Inscape_ao 7:9ab8809f9693 169 bool success;
Inscape_ao 7:9ab8809f9693 170 int setvalue = strtol(arg, NULL, 16);
Inscape_ao 7:9ab8809f9693 171 success = pDevRept->setConfigId(setvalue);
Inscape_ao 7:9ab8809f9693 172 if (!success) {
Inscape_ao 7:9ab8809f9693 173 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 174 return 1;
Inscape_ao 7:9ab8809f9693 175 }
Inscape_ao 7:9ab8809f9693 176 pC->reply();
Inscape_ao 7:9ab8809f9693 177 return 0;
Inscape_ao 7:9ab8809f9693 178 }
Inscape_ao 7:9ab8809f9693 179
Inscape_ao 7:9ab8809f9693 180 static int configWriteHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 181 {
Inscape_ao 7:9ab8809f9693 182 bool success;
Inscape_ao 7:9ab8809f9693 183 int setvalue = strtol(arg, NULL, 16);
Inscape_ao 7:9ab8809f9693 184 success = pDevRept->setConfigValue(setvalue);
Inscape_ao 7:9ab8809f9693 185 if (!success) {
Inscape_ao 7:9ab8809f9693 186 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 187 return 1;
Inscape_ao 7:9ab8809f9693 188 }
Inscape_ao 7:9ab8809f9693 189 pC->reply();
Inscape_ao 7:9ab8809f9693 190 return 0;
Inscape_ao 7:9ab8809f9693 191 }
Inscape_ao 7:9ab8809f9693 192
Inscape_ao 7:9ab8809f9693 193 static int configReadHandler(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 194 {
Inscape_ao 7:9ab8809f9693 195 bool success;
Inscape_ao 7:9ab8809f9693 196 int getvalue;
Inscape_ao 7:9ab8809f9693 197 Serial *pUart = pC->getCurrentUart();
Inscape_ao 7:9ab8809f9693 198
Inscape_ao 7:9ab8809f9693 199 success = pDevRept->getConfigValue(&getvalue);
Inscape_ao 7:9ab8809f9693 200 if (!success) {
Inscape_ao 7:9ab8809f9693 201 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 202 return 1;
Inscape_ao 7:9ab8809f9693 203 }
Inscape_ao 13:7cda5bef6390 204 uprintf(":%d CFG 0004 %04x\n", pC->getDeviceID(), getvalue);
Inscape_ao 7:9ab8809f9693 205 return 0;
Inscape_ao 7:9ab8809f9693 206 }
Inscape_ao 7:9ab8809f9693 207
Inscape_ao 7:9ab8809f9693 208 static int softResetHanlder(CommandParser *pC, char *arg, int exarg)
Inscape_ao 7:9ab8809f9693 209 {
Inscape_ao 7:9ab8809f9693 210 bool success;
Inscape_ao 7:9ab8809f9693 211 success = pDevRept->resetAllStatus();
Inscape_ao 7:9ab8809f9693 212 if (!success) {
Inscape_ao 7:9ab8809f9693 213 pC->reply(false, 3);
Inscape_ao 7:9ab8809f9693 214 return 1;
Inscape_ao 7:9ab8809f9693 215 }
Inscape_ao 7:9ab8809f9693 216 pC->reply();
Inscape_ao 7:9ab8809f9693 217 return 0;
Inscape_ao 7:9ab8809f9693 218 }
Inscape_ao 2:a694440145e9 219
Inscape_ao 2:a694440145e9 220 /****************************************************/
Inscape_ao 2:a694440145e9 221 /* Event Handlers */
Inscape_ao 2:a694440145e9 222 /****************************************************/
Inscape_ao 2:a694440145e9 223 /* Sample Command */
Inscape_ao 2:a694440145e9 224 static int sampleHanlder(CommandParser *pC, char *arg, int exarg)
Inscape_ao 2:a694440145e9 225 {
Inscape_ao 2:a694440145e9 226 pC->reply();
Inscape_ao 2:a694440145e9 227 return 0;
Inscape_ao 2:a694440145e9 228 }
Inscape_ao 2:a694440145e9 229
Inscape_ao 5:a37e3a15444b 230 /****************************************************/
Inscape_ao 5:a37e3a15444b 231 /* Event Handlers (SD Control) */
Inscape_ao 5:a37e3a15444b 232 /****************************************************/
Inscape_ao 5:a37e3a15444b 233 static int startSDStore(CommandParser *pC, char *arg, int exarg)
Inscape_ao 5:a37e3a15444b 234 {
Inscape_ao 5:a37e3a15444b 235 if (pSds->startFileWithTimeStamp("","txt") != true) {
Inscape_ao 5:a37e3a15444b 236 pC->reply(false, 1);
Inscape_ao 5:a37e3a15444b 237 return 1;
Inscape_ao 5:a37e3a15444b 238 }
Inscape_ao 5:a37e3a15444b 239 pC->reply();
Inscape_ao 5:a37e3a15444b 240 return 0;
Inscape_ao 5:a37e3a15444b 241 }
Inscape_ao 5:a37e3a15444b 242
Inscape_ao 5:a37e3a15444b 243 static int endSDStore(CommandParser *pC, char *arg, int exarg)
Inscape_ao 5:a37e3a15444b 244 {
Inscape_ao 5:a37e3a15444b 245 FILE *fp;
Inscape_ao 5:a37e3a15444b 246 if ((fp = pSds->getFilePointer()) == NULL) {
Inscape_ao 5:a37e3a15444b 247 /* NOP */
Inscape_ao 5:a37e3a15444b 248 pC->reply(false, 2);
Inscape_ao 5:a37e3a15444b 249 return 2;
Inscape_ao 5:a37e3a15444b 250 }
Inscape_ao 5:a37e3a15444b 251 fprintf(fp, "call endSDStore(%s)\n", pSds->getFileName());
Inscape_ao 5:a37e3a15444b 252 pSds->syncFile();
Inscape_ao 5:a37e3a15444b 253 pSds->closeFile();
Inscape_ao 5:a37e3a15444b 254 pC->reply();
Inscape_ao 5:a37e3a15444b 255 return 0;
Inscape_ao 5:a37e3a15444b 256 }
Inscape_ao 5:a37e3a15444b 257
Inscape_ao 5:a37e3a15444b 258 static int writeSDStore(CommandParser *pC, char *arg, int exarg)
Inscape_ao 5:a37e3a15444b 259 {
Inscape_ao 5:a37e3a15444b 260 FILE *fp;
Inscape_ao 5:a37e3a15444b 261 char curr[TimeManager::TimeStampLength + 1] = {0};
Inscape_ao 5:a37e3a15444b 262
Inscape_ao 5:a37e3a15444b 263 if ((fp = pSds->getFilePointer()) == NULL) {
Inscape_ao 5:a37e3a15444b 264 /* NOP */
Inscape_ao 5:a37e3a15444b 265 pC->reply(false, 2);
Inscape_ao 5:a37e3a15444b 266 return 2;
Inscape_ao 5:a37e3a15444b 267 }
Inscape_ao 5:a37e3a15444b 268 pTM->getTimeStamp(curr);
Inscape_ao 5:a37e3a15444b 269 fprintf(fp, "call writeSDStore at %s\n", curr);
Inscape_ao 5:a37e3a15444b 270 pSds->syncFile();
Inscape_ao 5:a37e3a15444b 271 pC->reply();
Inscape_ao 5:a37e3a15444b 272 return 0;
Inscape_ao 5:a37e3a15444b 273 }
Inscape_ao 5:a37e3a15444b 274
Inscape_ao 5:a37e3a15444b 275 /****************************************************/
Inscape_ao 5:a37e3a15444b 276 /* Event Handlers (Time Control) */
Inscape_ao 5:a37e3a15444b 277 /****************************************************/
Inscape_ao 2:a694440145e9 278 /* Time Set Command */
Inscape_ao 2:a694440145e9 279 static int setTimeHanlder(CommandParser *pC, char *arg, int exarg)
Inscape_ao 2:a694440145e9 280 {
Inscape_ao 2:a694440145e9 281 bool success = false;
Inscape_ao 2:a694440145e9 282 int setvalue = atoi(arg);
Inscape_ao 2:a694440145e9 283
Inscape_ao 2:a694440145e9 284 switch (exarg) {
Inscape_ao 2:a694440145e9 285 case TimeManager::SetTimeMethod::Year:
Inscape_ao 2:a694440145e9 286 case TimeManager::SetTimeMethod::Month:
Inscape_ao 2:a694440145e9 287 case TimeManager::SetTimeMethod::Day:
Inscape_ao 2:a694440145e9 288 case TimeManager::SetTimeMethod::Hour:
Inscape_ao 2:a694440145e9 289 case TimeManager::SetTimeMethod::Min:
Inscape_ao 2:a694440145e9 290 case TimeManager::SetTimeMethod::Sec:
Inscape_ao 2:a694440145e9 291 success = pTM->setCurrentTime(exarg,setvalue);
Inscape_ao 2:a694440145e9 292 break;
Inscape_ao 2:a694440145e9 293 }
Inscape_ao 2:a694440145e9 294 pC->reply(success, (success)? 0 : setvalue );
Inscape_ao 2:a694440145e9 295 return 0;
Inscape_ao 2:a694440145e9 296 }
Inscape_ao 2:a694440145e9 297
Inscape_ao 2:a694440145e9 298 /* Time Get Command */
Inscape_ao 2:a694440145e9 299 static int getTimeHanlder(CommandParser *pC, char *arg, int exarg)
Inscape_ao 2:a694440145e9 300 {
Inscape_ao 2:a694440145e9 301 int len;
Inscape_ao 2:a694440145e9 302 char timestamp[TimeManager::TimeStampLength + 1] = {0};
Inscape_ao 2:a694440145e9 303 Serial *pUart = pC->getCurrentUart();
Inscape_ao 2:a694440145e9 304 len = pTM->getTimeStamp(timestamp);
Inscape_ao 13:7cda5bef6390 305 uprintf(":%d RTS %04d %s\n", pC->getDeviceID(), len, timestamp);
Inscape_ao 2:a694440145e9 306 return 0;
Inscape_ao 2:a694440145e9 307 }