]> git.llucax.com Git - z.facultad/75.42/plaqui.git/blob - Model/src/simulator.cpp
9e84e8a257559f093b83e56fbd43000bcd2ae77f
[z.facultad/75.42/plaqui.git] / Model / src / simulator.cpp
1
2 #include "simulator.h"
3
4 using namespace PlaQui::Model;
5
6 Simulator::Simulator(const std::string &filename)
7 {
8         frame = 0;
9         /* Parseo de ejemplo de un XML desde archivo */
10         xmlDocPtr document;
11         document = xmlParseFile(filename.c_str());
12         if (document == NULL) {
13                 is_load_ok = false;
14                 std::cout << "Error cargando XML" << std::endl;
15                 return;
16         }
17
18         is_load_ok = true;
19
20         /* bien, el archivo se parseo bien! */
21         xmlNodePtr nodo, items;
22         nodo = document->children;
23
24         if (strcmp((char *)nodo->name, "planta") == 0) {
25                 items = nodo->children;
26                 while (items != NULL) {
27                         if (items->type == XML_ELEMENT_NODE) {
28                                 if (xmlStrcmp(items->name, BAD_CAST"bomba")==0) {
29                                         loadBomba(items);
30                                 } else if ((xmlStrcmp(items->name, BAD_CAST"tubo")==0)||(xmlStrcmp(items->name, BAD_CAST"codo")==0)) {
31                                         loadConduct(items);
32                                 } else if (xmlStrcmp(items->name, BAD_CAST"exclusa")==0) {
33                                         loadExclusa(items);
34                                 } else if (xmlStrcmp(items->name, BAD_CAST"tanque")==0) {
35                                         loadTank(items);
36                                 } else if (xmlStrcmp(items->name, BAD_CAST"empalme")==0) {
37                                         loadUnion(items);
38                                 } else if (xmlStrcmp(items->name, BAD_CAST"drenaje")==0) {
39                                         loadDrain(items);
40                                 } else if (xmlStrcmp(items->name, BAD_CAST"and")==0) {
41                                         loadAnd(items);
42                                 } else if (xmlStrcmp(items->name, BAD_CAST"or")==0) {
43                                         loadOr(items);
44                                 } else if (xmlStrcmp(items->name, BAD_CAST"not")==0) {
45                                         loadNot(items);
46                                 }
47                         }
48                         items = items->next;
49                 }
50                 // Bien, la planta esta cargada, conectemos todo!!
51                 do_connections(nodo->children);
52                 do_logic_connetions(nodo->children);
53         }
54         xmlFreeDoc(document);
55 }
56
57 Simulator::~Simulator()
58 {
59         std::list<PlantItem *>::iterator i = items.begin();
60         PlantItem *o;
61
62         while (i != items.end()) {
63                 o = (*i);
64                 items.remove(o);
65                 delete o;
66                 i = items.begin();
67         }
68 }
69
70 void Simulator::add_pump(const std::string &name, float max_flow, RGB color)
71 {
72         Pump *p = new Pump(name);
73         p->set_max_flow(max_flow);
74         p->set_color(color);
75         pump_lst.push_back(p);
76         items.push_back(p);
77 }
78
79 void Simulator::add_union(const std::string &name, float max_flow)
80 {
81         Union *u = new Union(name);
82         u->set_max_flow(max_flow);
83         union_lst.push_back(u);
84         items.push_back(u);
85 }
86
87 void Simulator::add_splitter(const std::string &name, float max_flow)
88 {
89         Splitter *p = new Splitter(name);
90         p->set_max_flow(max_flow);
91         split_lst.push_back(p);
92         items.push_back(p);
93 }
94
95 void Simulator::add_conduct(const std::string &name, float flujo)
96 {
97         Conduct *p = new Conduct(name);
98         p->set_max_flow(flujo);
99         conduct_lst.push_back(p);
100         items.push_back(p);
101 }
102
103 void Simulator::add_exclusa(const std::string &name, bool open)
104 {
105         Exclusa *p = new Exclusa(name);
106         if (!open)
107                 p->close();
108         exclusa_lst.push_back(p);
109         items.push_back(p);
110 }
111
112 void Simulator::add_tank(const std::string &name, float capacity, float initial, RGB color)
113 {
114         Tank *p = new Tank(name);
115         p->set_capacity(capacity);
116         p->set_max_flow(initial);
117         p->set_litros(initial);
118         p->set_color(color);
119         tank_lst.push_back(p);
120         items.push_back(p);
121 }
122
123 void Simulator::add_drainage(const std::string &name)
124 {
125         Drainage *p = new Drainage(name);
126         drainage_lst.push_back(p);
127         items.push_back(p);
128 }
129
130 bool Simulator::connect(const std::string &name1, const std::string &name2, int flag)
131 {
132         IConector *o1, *o2;
133         o1 = find(name1);
134         o2 = find(name2);
135
136         if ((o1 == NULL) || (o2 == NULL)) {
137                 // NO SE PUDO CONECTAR!, FALTAN ITEMS!!
138                 return false;
139         }
140
141         bool b;
142         if (flag == IConector::OUT) {
143                 b = o1->connect(o2, IConector::OUT);
144                 b = b && o2->connect(o1, IConector::IN);
145         } else {
146                 b = o1->connect(o2, IConector::IN);
147                 b = b && o2->connect(o1, IConector::OUT);
148         }
149         
150         return b;
151 }
152
153 void Simulator::simulate()
154 {
155         // Actualizo
156         std::list<Pump *>::iterator i1;
157         for(i1=pump_lst.begin(); i1!=pump_lst.end(); i1++)
158                 (*i1)->update();
159
160         std::list<PlantItem *>::iterator i2;
161         for(i2=items.begin(); i2!=items.end(); i2++) 
162                 (*i2)->update();
163         
164         for(i2=items.begin(); i2!=items.end(); i2++) 
165                 (*i2)->simulate();
166
167         frame++;
168 }
169
170 IConector *Simulator::find(const std::string &name)
171 {
172         // Busco el item, aca no me importa de que tipo es!
173         std::list<PlantItem *>::iterator i;
174         for(i=items.begin(); i!=items.end(); i++)
175                 if ((*i)->get_name() == name)
176                         return *i;
177         return NULL;
178 }
179
180 LogicControl *Simulator::find_logic(const std::string &name)
181 {
182         // Busco el item, aca no me importa de que tipo es!
183         std::list<LogicControl *>::iterator i;
184         for(i=control_lst.begin(); i!=control_lst.end(); i++)
185                 if ((*i)->get_name() == name)
186                         return *i;
187         return NULL;
188 }
189
190 bool Simulator::set_open(const std::string &name, bool open)
191 {
192         // Busco el elemento, usando RTTI :-(
193         IConector *tmp = find(name);
194         Pump *p;
195         Exclusa *e;
196         if ((p = dynamic_cast<Pump*>(tmp))) {
197                 if (open) {
198                         p->activate();
199                 } else {
200                         p->deactivate();
201                 }
202         } else if ((e = dynamic_cast<Exclusa*>(tmp))) {
203                 if (open) {
204                         e->open();
205                 } else {
206                         e->close();
207                 }
208         } else {
209                 return false;
210         }
211 }
212
213 void Simulator::loadBomba(xmlNodePtr nodo)
214 {
215         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
216         int orientacion=0, x, y;
217         RGB color;
218         float flujo;
219
220         nodo = nodo->children;
221         while (nodo != NULL) {
222                 if (nodo->type == XML_ELEMENT_NODE) {
223                         if (xmlStrcmp(nodo->name, BAD_CAST"orientacion") == 0) {
224                                 orientacion = atoi( (char *)XML_GET_CONTENT(nodo->children) );
225                         } else if (xmlStrcmp(nodo->name, BAD_CAST"x") == 0) {
226                                 x = atoi( (char *)XML_GET_CONTENT(nodo->children) );
227                         } else if (xmlStrcmp(nodo->name, BAD_CAST"y") == 0) {
228                                 y = atoi( (char *)XML_GET_CONTENT(nodo->children) );
229                         } else if (xmlStrcmp(nodo->name, BAD_CAST"entrega") == 0) {
230                                 flujo = atof( (char *)XML_GET_CONTENT(nodo->children) );
231                         } else if (xmlStrcmp(nodo->name, BAD_CAST"color") == 0) {
232                                 color = loadRGB(nodo->children);
233                         }
234                 }
235                 nodo = nodo->next;
236         }
237
238         add_pump(name, flujo, color);
239 }
240
241 void Simulator::loadConduct(xmlNodePtr nodo)
242 {
243         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
244         int orientacion=0, x, y;
245         float flujo;
246
247         nodo = nodo->children;
248         while (nodo != NULL) {
249                 if (nodo->type == XML_ELEMENT_NODE) {
250                         if (xmlStrcmp(nodo->name, BAD_CAST"orientacion") == 0) {
251                                 orientacion = atoi( (char *)XML_GET_CONTENT(nodo->children) );
252                         } else if (xmlStrcmp(nodo->name, BAD_CAST"x") == 0) {
253                                 x = atoi( (char *)XML_GET_CONTENT(nodo->children) );
254                         } else if (xmlStrcmp(nodo->name, BAD_CAST"y") == 0) {
255                                 y = atoi( (char *)XML_GET_CONTENT(nodo->children) );
256                         } else if (xmlStrcmp(nodo->name, BAD_CAST"caudal") == 0) {
257                                 flujo = atof( (char *)XML_GET_CONTENT(nodo->children) );
258                         }
259                         
260
261                 }
262                 nodo = nodo->next;
263         }
264
265         // listo, ya recolecte todos los datos, ahora creo el objeto!
266         add_conduct(name, flujo);
267 }
268
269 void Simulator::loadExclusa(xmlNodePtr nodo)
270 {
271         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
272         int orientacion=0, x, y;
273         std::string open;
274
275         nodo = nodo->children;
276         while (nodo != NULL) {
277                 if (nodo->type == XML_ELEMENT_NODE) {
278                         if (xmlStrcmp(nodo->name, BAD_CAST"orientacion") == 0) {
279                                 orientacion = atoi( (char *)XML_GET_CONTENT(nodo->children) );
280                         } else if (xmlStrcmp(nodo->name, BAD_CAST"x") == 0) {
281                                 x = atoi( (char *)XML_GET_CONTENT(nodo->children) );
282                         } else if (xmlStrcmp(nodo->name, BAD_CAST"y") == 0) {
283                                 y = atoi( (char *)XML_GET_CONTENT(nodo->children) );
284                         } else if (xmlStrcmp(nodo->name, BAD_CAST"estado") == 0) {
285                                 open = (char *)XML_GET_CONTENT(nodo->children);
286                         }
287                 }
288                 nodo = nodo->next;
289         }
290
291         // listo, ya recolecte todos los datos, ahora creo el objeto!
292         add_exclusa(name, open == "1");
293 }
294
295 void Simulator::loadTank(xmlNodePtr nodo)
296 {
297         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
298         int orientacion=0, x, y;
299         float capacidad, inicial;
300         RGB color;
301
302         nodo = nodo->children;
303         while (nodo != NULL) {
304                 if (nodo->type == XML_ELEMENT_NODE) {
305                         if (xmlStrcmp(nodo->name, BAD_CAST"orientacion") == 0) {
306                                 orientacion = atoi( (char *)XML_GET_CONTENT(nodo->children) );
307                         } else if (xmlStrcmp(nodo->name, BAD_CAST"x") == 0) {
308                                 x = atoi( (char *)XML_GET_CONTENT(nodo->children) );
309                         } else if (xmlStrcmp(nodo->name, BAD_CAST"y") == 0) {
310                                 y = atoi( (char *)XML_GET_CONTENT(nodo->children) );
311                         } else if (xmlStrcmp(nodo->name, BAD_CAST"capacidad") == 0) {
312                                 capacidad = atoi( (char *)XML_GET_CONTENT(nodo->children) );
313                         } else if (xmlStrcmp(nodo->name, BAD_CAST"inicial") == 0) {
314                                 inicial = atof( (char *)XML_GET_CONTENT(nodo->children) );
315                         } else if (xmlStrcmp(nodo->name, BAD_CAST"") == 0) {
316                                 color = loadRGB(nodo->children);
317                         }
318                 }
319                 nodo = nodo->next;
320         }
321
322         // listo, ya recolecte todos los datos, ahora creo el objeto!
323         add_tank(name, capacidad, inicial, color);
324 }
325
326 void Simulator::loadUnion(xmlNodePtr nodo)
327 {
328         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
329         int orientacion=0, x, y;
330         float flow;
331         std::string type;
332
333         nodo = nodo->children;
334         while (nodo != NULL) {
335                 if (nodo->type == XML_ELEMENT_NODE) {
336                         if (xmlStrcmp(nodo->name, BAD_CAST"orientacion") == 0) {
337                                 orientacion = atoi( (char *)XML_GET_CONTENT(nodo->children) );
338                         } else if (xmlStrcmp(nodo->name, BAD_CAST"x") == 0) {
339                                 x = atoi( (char *)XML_GET_CONTENT(nodo->children) );
340                         } else if (xmlStrcmp(nodo->name, BAD_CAST"y") == 0) {
341                                 y = atoi( (char *)XML_GET_CONTENT(nodo->children) );
342                         } else if (xmlStrcmp(nodo->name, BAD_CAST"caudal") == 0) {
343                                 flow = atof( (char *)XML_GET_CONTENT(nodo->children) );
344                         } else if (xmlStrcmp(nodo->name, BAD_CAST"tipo") == 0) {
345                                 type = (char *)XML_GET_CONTENT(nodo->children);
346                         }
347                 }
348                 nodo = nodo->next;
349         }
350
351         // listo, ya recolecte todos los datos, ahora creo el objeto!
352         if (type == "union")
353                 add_union(name, flow);
354         else
355                 add_splitter(name, flow);
356 }
357
358 void Simulator::loadDrain(xmlNodePtr nodo)
359 {
360         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
361
362         add_drainage(name);
363 }
364
365 void Simulator::loadNot(xmlNodePtr nodo)
366 {
367         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
368         std::string id = (char *)xmlGetProp(nodo, BAD_CAST"id");
369         int orientacion=0, x, y;
370
371         nodo = nodo->children;
372         while (nodo != NULL) {
373                 if (nodo->type == XML_ELEMENT_NODE) {
374                         if (xmlStrcmp(nodo->name, BAD_CAST"salida") == 0) {
375                                 //p->out_lines.push_back((char *)XML_GET_CONTENT(nodo->children));
376                         } else if (xmlStrcmp(nodo->name, BAD_CAST"entrada") == 0) {
377                                 //p->in_lines.push_back((char *)XML_GET_CONTENT(nodo->children));
378                         }
379                 }
380                 nodo = nodo->next;
381         }
382
383         Not *n = new Not();
384         n->set_name(name);
385         control_lst.push_back(n);
386 }
387
388 void Simulator::loadOr(xmlNodePtr nodo)
389 {
390         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
391         std::string id = (char *)xmlGetProp(nodo, BAD_CAST"id");
392
393         nodo = nodo->children;
394         while (nodo != NULL) {
395                 if (nodo->type == XML_ELEMENT_NODE) {
396                         if (xmlStrcmp(nodo->name, BAD_CAST"salida") == 0) {
397                                 //p->out_lines.push_back((char *)XML_GET_CONTENT(nodo->children));
398                         } else if (xmlStrcmp(nodo->name, BAD_CAST"entrada") == 0) {
399                                 //p->in_lines.push_back((char *)XML_GET_CONTENT(nodo->children));
400                         }
401                 }
402                 nodo = nodo->next;
403         }
404
405         Or *n = new Or();
406         n->set_name(name);
407         control_lst.push_back(n);
408 }
409
410 void Simulator::loadAnd(xmlNodePtr nodo)
411 {
412         std::string name = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
413         std::string id = (char *)xmlGetProp(nodo, BAD_CAST"id");
414         int orientacion=0, x, y;
415         
416         nodo = nodo->children;
417         while (nodo != NULL) {
418                 if (nodo->type == XML_ELEMENT_NODE) {
419                         if (xmlStrcmp(nodo->name, BAD_CAST"salida") == 0) {
420                         //      p->out_lines.push_back((char *)XML_GET_CONTENT(nodo->children));
421                         } else if (xmlStrcmp(nodo->name, BAD_CAST"entrada") == 0) {
422                         //      p->in_lines.push_back((char *)XML_GET_CONTENT(nodo->children));
423                         }
424                 }
425                 nodo = nodo->next;
426         }
427
428         And *n = new And();
429         n->set_name(name);
430         control_lst.push_back(n);
431 }
432
433 void Simulator::do_connections(xmlNodePtr nodo)
434 {
435         // Intengo conectar los elementos :)
436         IConector *current_item, *to_connect;
437         xmlNodePtr props; // propiedades del item actual
438         xmlNodePtr conector1, conector2, conector3;
439
440         while (nodo != NULL) {
441                 if (nodo->type != XML_ELEMENT_NODE) {
442                         nodo = nodo->next;
443                         continue;
444                 }
445                 // obtengo el items actual por su nombre
446                 std::string s = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
447                 current_item = find((char *)xmlGetProp(nodo, BAD_CAST"nombre"));
448                 props = nodo->children;
449                 conector3 = conector2 = conector1 = NULL;
450                 while (props != NULL) {
451                         if (nodo->type == XML_ELEMENT_NODE) {
452                                 if (xmlStrcmp(props->name, BAD_CAST"conector") == 0) {
453                                         xmlNodePtr temp = props->children;
454                                         while ((temp != NULL) && (conector1 == NULL))
455                                                 if (temp->type == XML_ELEMENT_NODE) {
456                                                         conector1 = temp;
457                                                         temp = temp->next;
458                                                         break;
459                                                 } else {
460                                                         temp = temp->next;
461                                                 }
462                                         while ((temp != NULL) && (conector2 == NULL))
463                                                 if (temp->type == XML_ELEMENT_NODE) {
464                                                         conector2 = temp;
465                                                         temp = temp->next;
466                                                         break;
467                                                 } else {
468                                                         temp = temp->next;
469                                                 }
470                                         while ((temp != NULL) && (conector3 == NULL))
471                                                 if (temp->type == XML_ELEMENT_NODE) {
472                                                         conector3 = temp;
473                                                         temp = temp->next;
474                                                         break;
475                                                 } else {
476                                                         temp = temp->next;
477                                                 }
478                                 }
479                         }
480                         props = props->next;
481                 }
482                 // Bien, conector1 y 2 deberian estar apuntando a <entrada> y/o <salida>
483                 if (conector1 != NULL) {
484                         // si, aca hay un conector!, veamos cual es
485                         if (xmlStrcmp(conector1->name, BAD_CAST"entrada") == 0) {
486                                 // bien, es a la entrada!, obtengo el item al cual lo tengo que conectar
487                                 to_connect = find((char *)XML_GET_CONTENT(conector1->children));
488                                 // y lo conecto
489                                 current_item->connect(to_connect, IConector::IN);
490                         } else if (xmlStrcmp(conector1->name, BAD_CAST"salida") == 0) {
491                                 // Era a salida, es casi lo mismo que arriba 
492                                 to_connect = find((char *)XML_GET_CONTENT(conector1->children));
493                                 current_item->connect(to_connect, IConector::OUT);
494                         }
495                 }
496                 if (conector2 != NULL) {
497                         // si, aca hay un conector!, veamos cual es
498                         if (xmlStrcmp(conector2->name, BAD_CAST"entrada") == 0) {
499                                 // bien, es a la entrada!, obtengo el item al cual lo tengo que conectar
500                                 to_connect = find((char *)XML_GET_CONTENT(conector2->children));
501                                 // y lo conecto
502                                 current_item->connect(to_connect, IConector::IN);
503                         } else if (xmlStrcmp(conector2->name, BAD_CAST"salida") == 0) {
504                                 // Era a salida, es casi lo mismo que arriba 
505                                 to_connect = find((char *)XML_GET_CONTENT(conector2->children));
506                                 current_item->connect(to_connect, IConector::OUT);
507                         }
508                 }
509                 if (conector3 != NULL) {
510                         // si, aca hay un conector!, veamos cual es
511                         if (xmlStrcmp(conector3->name, BAD_CAST"entrada") == 0) {
512                                 // bien, es a la entrada!, obtengo el item al cual lo tengo que conectar
513                                 to_connect = find((char *)XML_GET_CONTENT(conector3->children));
514                                 // y lo conecto
515                                 if (!current_item->connect(to_connect, IConector::IN)) {
516                                         std::cout << s << " Error al conectar " << std::endl;
517                                 }
518                         } else if (xmlStrcmp(conector3->name, BAD_CAST"salida") == 0) {
519                                 // Era a salida, es casi lo mismo que arriba 
520                                 to_connect = find((char *)XML_GET_CONTENT(conector3->children));
521                                 if (!current_item->connect(to_connect, IConector::OUT)) {
522                                         std::cout << s << " Error al conectar " << std::endl;
523                                 }
524                         }
525                 }
526                 nodo = nodo->next;
527         }
528         // Fin de las conexiones
529 }
530
531 std::string Simulator::get_state_as_xml()
532 {
533         std::stringstream out;
534
535         // XML Header
536         out << "<?xml version=\"1.0\" ?>" << std::endl;
537
538         out << "<plantstatus frame=\"" << frame << "\">" << std::endl;
539         
540         std::list<PlantItem *>::iterator i2;
541         for(i2=items.begin(); i2!=items.end(); i2++)
542                 (*i2)->get_state_as_xml(out);
543
544         out << "</plantstatus>";
545         return out.str();;
546 }
547
548 RGB Simulator::loadRGB(xmlNodePtr nodo)
549 {
550         unsigned r,g,b;
551         while (nodo != NULL) {
552                 if (nodo->type == XML_ELEMENT_NODE) {
553                         if (xmlStrcmp(nodo->name, BAD_CAST"rojo")==0)
554                                 r = atoi( (char *)XML_GET_CONTENT(nodo->children) );
555                         if (xmlStrcmp(nodo->name, BAD_CAST"verde")==0)
556                                 g = atoi( (char *)XML_GET_CONTENT(nodo->children) );
557                         if (xmlStrcmp(nodo->name, BAD_CAST"azul")==0)
558                                 b = atoi( (char *)XML_GET_CONTENT(nodo->children) );
559                 }
560                 nodo = nodo->next;
561         }
562         return RGB(r,g,b);
563 }
564
565 void Simulator::do_logic_connetions(xmlNodePtr nodo)
566 {
567         LogicControl *current;
568
569         while (nodo != NULL) {
570                 if (nodo->type != XML_ELEMENT_NODE) {
571                         nodo = nodo->next;
572                         continue;
573                 }
574                 // obtengo el items actual por su nombre
575                 if (xmlStrcmp(nodo->name, BAD_CAST"and") == 0) {
576                         std::string s = (char *)xmlGetProp(nodo, BAD_CAST"nombre");
577                         current = find_logic((char *)xmlGetProp(nodo, BAD_CAST"nombre"));
578                         connect_logic(current, nodo->children);
579                 }
580                 nodo = nodo->next;
581         }
582 }
583
584 void Simulator::connect_logic(LogicControl *current, xmlNodePtr nodo)
585 {
586         Control *item;
587         while (nodo != NULL) {
588                 if (nodo->type != XML_ELEMENT_NODE) {
589                         nodo = nodo->next;
590                         continue;
591                 }
592                 if (xmlStrcmp(nodo->name, BAD_CAST"entrada") == 0) {
593                         item = dynamic_cast<Control *>(find((char *)XML_GET_CONTENT(nodo->children)));
594                         if (item != NULL) {
595                                 current->connect( item->get_logic_output(), IConector::IN );
596                         } else {
597                                 std::cout << "ERROR : Item no es tipo Control!!" << std::endl;
598                         }
599                 } else if (xmlStrcmp(nodo->name, BAD_CAST"salida") == 0) {
600                         item = dynamic_cast<Control *>(find((char *)XML_GET_CONTENT(nodo->children)));
601                         if (item != NULL) {
602                                 item->get_logic_input()->connect( current, IConector::IN );
603                         } else {
604                                 std::cout << "ERROR : Item no es tipo Control!!" << std::endl;
605                         }
606                 }
607
608                 nodo = nodo->next;
609         }
610 }
611