|
@@ -184,6 +184,14 @@ menuFunc_t callbackFunc;
|
184
|
184
|
// place-holders for Ki and Kd edits
|
185
|
185
|
float raw_Ki, raw_Kd;
|
186
|
186
|
|
|
187
|
+static void lcd_goto_menu(menuFunc_t menu, const uint32_t encoder=0, const bool feedback=true) {
|
|
188
|
+ if (currentMenu != menu) {
|
|
189
|
+ currentMenu = menu;
|
|
190
|
+ encoderPosition = encoder;
|
|
191
|
+ if (feedback) lcd_quick_feedback();
|
|
192
|
+ }
|
|
193
|
+}
|
|
194
|
+
|
187
|
195
|
/* Main status screen. It's up to the implementation specific part to show what is needed. As this is very display dependent */
|
188
|
196
|
static void lcd_status_screen()
|
189
|
197
|
{
|
|
@@ -218,9 +226,7 @@ static void lcd_status_screen()
|
218
|
226
|
|
219
|
227
|
if (current_click)
|
220
|
228
|
{
|
221
|
|
- currentMenu = lcd_main_menu;
|
222
|
|
- encoderPosition = 0;
|
223
|
|
- lcd_quick_feedback();
|
|
229
|
+ lcd_goto_menu(lcd_main_menu);
|
224
|
230
|
lcd_implementation_init(); // to maybe revive the LCD if static electricity killed it.
|
225
|
231
|
#ifdef FILAMENT_LCD_DISPLAY
|
226
|
232
|
message_millis=millis(); //get status message to show up for a while
|
|
@@ -263,10 +269,8 @@ static void lcd_status_screen()
|
263
|
269
|
#ifdef ULTIPANEL
|
264
|
270
|
static void lcd_return_to_status()
|
265
|
271
|
{
|
266
|
|
- encoderPosition = 0;
|
267
|
|
- currentMenu = lcd_status_screen;
|
|
272
|
+ lcd_goto_menu(lcd_status_screen, 0, false);
|
268
|
273
|
}
|
269
|
|
-
|
270
|
274
|
static void lcd_sdcard_pause()
|
271
|
275
|
{
|
272
|
276
|
card.pauseSDPrint();
|
|
@@ -357,65 +361,20 @@ void lcd_set_home_offsets()
|
357
|
361
|
|
358
|
362
|
|
359
|
363
|
#ifdef BABYSTEPPING
|
360
|
|
-static void lcd_babystep_x()
|
361
|
|
-{
|
362
|
|
- if (encoderPosition != 0)
|
363
|
|
- {
|
364
|
|
- babystepsTodo[X_AXIS]+=(int)encoderPosition;
|
365
|
|
- encoderPosition=0;
|
366
|
|
- lcdDrawUpdate = 1;
|
367
|
|
- }
|
368
|
|
- if (lcdDrawUpdate)
|
369
|
|
- {
|
370
|
|
- lcd_implementation_drawedit(PSTR(MSG_BABYSTEPPING_X),"");
|
371
|
|
- }
|
372
|
|
- if (LCD_CLICKED)
|
373
|
|
- {
|
374
|
|
- lcd_quick_feedback();
|
375
|
|
- currentMenu = lcd_tune_menu;
|
376
|
|
- encoderPosition = 0;
|
377
|
|
- }
|
378
|
|
-}
|
379
|
364
|
|
380
|
|
-static void lcd_babystep_y()
|
381
|
|
-{
|
382
|
|
- if (encoderPosition != 0)
|
383
|
|
- {
|
384
|
|
- babystepsTodo[Y_AXIS]+=(int)encoderPosition;
|
385
|
|
- encoderPosition=0;
|
386
|
|
- lcdDrawUpdate = 1;
|
387
|
|
- }
|
388
|
|
- if (lcdDrawUpdate)
|
389
|
|
- {
|
390
|
|
- lcd_implementation_drawedit(PSTR(MSG_BABYSTEPPING_Y),"");
|
|
365
|
+ static void _lcd_babystep(int axis, const char *msg) {
|
|
366
|
+ if (encoderPosition != 0) {
|
|
367
|
+ babystepsTodo[axis] += (int)encoderPosition;
|
|
368
|
+ encoderPosition = 0;
|
|
369
|
+ lcdDrawUpdate = 1;
|
391
|
370
|
}
|
392
|
|
- if (LCD_CLICKED)
|
393
|
|
- {
|
394
|
|
- lcd_quick_feedback();
|
395
|
|
- currentMenu = lcd_tune_menu;
|
396
|
|
- encoderPosition = 0;
|
397
|
|
- }
|
398
|
|
-}
|
|
371
|
+ if (lcdDrawUpdate) lcd_implementation_drawedit(PSTR(msg), "");
|
|
372
|
+ if (LCD_CLICKED) lcd_goto_menu(lcd_tune_menu);
|
|
373
|
+ }
|
|
374
|
+ static void lcd_babystep_x() { _lcd_babystep(X_AXIS, MSG_BABYSTEPPING_X); }
|
|
375
|
+ static void lcd_babystep_y() { _lcd_babystep(Y_AXIS, MSG_BABYSTEPPING_Y); }
|
|
376
|
+ static void lcd_babystep_z() { _lcd_babystep(Z_AXIS, MSG_BABYSTEPPING_Z); }
|
399
|
377
|
|
400
|
|
-static void lcd_babystep_z()
|
401
|
|
-{
|
402
|
|
- if (encoderPosition != 0)
|
403
|
|
- {
|
404
|
|
- babystepsTodo[Z_AXIS]+=BABYSTEP_Z_MULTIPLICATOR*(int)encoderPosition;
|
405
|
|
- encoderPosition=0;
|
406
|
|
- lcdDrawUpdate = 1;
|
407
|
|
- }
|
408
|
|
- if (lcdDrawUpdate)
|
409
|
|
- {
|
410
|
|
- lcd_implementation_drawedit(PSTR(MSG_BABYSTEPPING_Z),"");
|
411
|
|
- }
|
412
|
|
- if (LCD_CLICKED)
|
413
|
|
- {
|
414
|
|
- lcd_quick_feedback();
|
415
|
|
- currentMenu = lcd_tune_menu;
|
416
|
|
- encoderPosition = 0;
|
417
|
|
- }
|
418
|
|
-}
|
419
|
378
|
#endif //BABYSTEPPING
|
420
|
379
|
|
421
|
380
|
static void lcd_tune_menu()
|
|
@@ -644,96 +603,28 @@ static void lcd_prepare_menu()
|
644
|
603
|
float move_menu_scale;
|
645
|
604
|
static void lcd_move_menu_axis();
|
646
|
605
|
|
647
|
|
-static void lcd_move_x()
|
648
|
|
-{
|
649
|
|
- if (encoderPosition != 0)
|
650
|
|
- {
|
651
|
|
- refresh_cmd_timeout();
|
652
|
|
- current_position[X_AXIS] += float((int)encoderPosition) * move_menu_scale;
|
653
|
|
- if (min_software_endstops && current_position[X_AXIS] < X_MIN_POS)
|
654
|
|
- current_position[X_AXIS] = X_MIN_POS;
|
655
|
|
- if (max_software_endstops && current_position[X_AXIS] > X_MAX_POS)
|
656
|
|
- current_position[X_AXIS] = X_MAX_POS;
|
657
|
|
- encoderPosition = 0;
|
658
|
|
- #ifdef DELTA
|
659
|
|
- calculate_delta(current_position);
|
660
|
|
- plan_buffer_line(delta[X_AXIS], delta[Y_AXIS], delta[Z_AXIS], current_position[E_AXIS], manual_feedrate[X_AXIS]/60, active_extruder);
|
661
|
|
- #else
|
662
|
|
- plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], manual_feedrate[X_AXIS]/60, active_extruder);
|
663
|
|
- #endif
|
664
|
|
- lcdDrawUpdate = 1;
|
665
|
|
- }
|
666
|
|
- if (lcdDrawUpdate)
|
667
|
|
- {
|
668
|
|
- lcd_implementation_drawedit(PSTR("X"), ftostr31(current_position[X_AXIS]));
|
669
|
|
- }
|
670
|
|
- if (LCD_CLICKED)
|
671
|
|
- {
|
672
|
|
- lcd_quick_feedback();
|
673
|
|
- currentMenu = lcd_move_menu_axis;
|
674
|
|
- encoderPosition = 0;
|
675
|
|
- }
|
676
|
|
-}
|
677
|
|
-static void lcd_move_y()
|
678
|
|
-{
|
679
|
|
- if (encoderPosition != 0)
|
680
|
|
- {
|
681
|
|
- refresh_cmd_timeout();
|
682
|
|
- current_position[Y_AXIS] += float((int)encoderPosition) * move_menu_scale;
|
683
|
|
- if (min_software_endstops && current_position[Y_AXIS] < Y_MIN_POS)
|
684
|
|
- current_position[Y_AXIS] = Y_MIN_POS;
|
685
|
|
- if (max_software_endstops && current_position[Y_AXIS] > Y_MAX_POS)
|
686
|
|
- current_position[Y_AXIS] = Y_MAX_POS;
|
687
|
|
- encoderPosition = 0;
|
688
|
|
- #ifdef DELTA
|
689
|
|
- calculate_delta(current_position);
|
690
|
|
- plan_buffer_line(delta[X_AXIS], delta[Y_AXIS], delta[Z_AXIS], current_position[E_AXIS], manual_feedrate[Y_AXIS]/60, active_extruder);
|
691
|
|
- #else
|
692
|
|
- plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], manual_feedrate[Y_AXIS]/60, active_extruder);
|
693
|
|
- #endif
|
694
|
|
- lcdDrawUpdate = 1;
|
695
|
|
- }
|
696
|
|
- if (lcdDrawUpdate)
|
697
|
|
- {
|
698
|
|
- lcd_implementation_drawedit(PSTR("Y"), ftostr31(current_position[Y_AXIS]));
|
699
|
|
- }
|
700
|
|
- if (LCD_CLICKED)
|
701
|
|
- {
|
702
|
|
- lcd_quick_feedback();
|
703
|
|
- currentMenu = lcd_move_menu_axis;
|
704
|
|
- encoderPosition = 0;
|
705
|
|
- }
|
706
|
|
-}
|
707
|
|
-static void lcd_move_z()
|
708
|
|
-{
|
709
|
|
- if (encoderPosition != 0)
|
710
|
|
- {
|
711
|
|
- refresh_cmd_timeout();
|
712
|
|
- current_position[Z_AXIS] += float((int)encoderPosition) * move_menu_scale;
|
713
|
|
- if (min_software_endstops && current_position[Z_AXIS] < Z_MIN_POS)
|
714
|
|
- current_position[Z_AXIS] = Z_MIN_POS;
|
715
|
|
- if (max_software_endstops && current_position[Z_AXIS] > Z_MAX_POS)
|
716
|
|
- current_position[Z_AXIS] = Z_MAX_POS;
|
717
|
|
- encoderPosition = 0;
|
718
|
|
- #ifdef DELTA
|
719
|
|
- calculate_delta(current_position);
|
720
|
|
- plan_buffer_line(delta[X_AXIS], delta[Y_AXIS], delta[Z_AXIS], current_position[E_AXIS], manual_feedrate[Z_AXIS]/60, active_extruder);
|
721
|
|
- #else
|
722
|
|
- plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], manual_feedrate[Z_AXIS]/60, active_extruder);
|
723
|
|
- #endif
|
724
|
|
- lcdDrawUpdate = 1;
|
725
|
|
- }
|
726
|
|
- if (lcdDrawUpdate)
|
727
|
|
- {
|
728
|
|
- lcd_implementation_drawedit(PSTR("Z"), ftostr31(current_position[Z_AXIS]));
|
729
|
|
- }
|
730
|
|
- if (LCD_CLICKED)
|
731
|
|
- {
|
732
|
|
- lcd_quick_feedback();
|
733
|
|
- currentMenu = lcd_move_menu_axis;
|
734
|
|
- encoderPosition = 0;
|
735
|
|
- }
|
|
606
|
+static void _lcd_move(const char *name, int axis, int min, int max) {
|
|
607
|
+ if (encoderPosition != 0) {
|
|
608
|
+ refresh_cmd_timeout();
|
|
609
|
+ current_position[axis] += float((int)encoderPosition) * move_menu_scale;
|
|
610
|
+ if (min_software_endstops && current_position[axis] < min) current_position[axis] = min;
|
|
611
|
+ if (max_software_endstops && current_position[axis] > max) current_position[axis] = max;
|
|
612
|
+ encoderPosition = 0;
|
|
613
|
+ #ifdef DELTA
|
|
614
|
+ calculate_delta(current_position);
|
|
615
|
+ plan_buffer_line(delta[X_AXIS], delta[Y_AXIS], delta[Z_AXIS], current_position[E_AXIS], manual_feedrate[axis]/60, active_extruder);
|
|
616
|
+ #else
|
|
617
|
+ plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], manual_feedrate[axis]/60, active_extruder);
|
|
618
|
+ #endif
|
|
619
|
+ lcdDrawUpdate = 1;
|
|
620
|
+ }
|
|
621
|
+ if (lcdDrawUpdate) lcd_implementation_drawedit(name, ftostr31(current_position[axis]));
|
|
622
|
+ if (LCD_CLICKED) lcd_goto_menu(lcd_move_menu_axis);
|
736
|
623
|
}
|
|
624
|
+static void lcd_move_x() { _lcd_move(PSTR("X"), X_AXIS, X_MIN_POS, X_MAX_POS); }
|
|
625
|
+static void lcd_move_y() { _lcd_move(PSTR("Y"), Y_AXIS, Y_MIN_POS, Y_MAX_POS); }
|
|
626
|
+static void lcd_move_z() { _lcd_move(PSTR("Z"), Z_AXIS, Z_MIN_POS, Z_MAX_POS); }
|
|
627
|
+
|
737
|
628
|
static void lcd_move_e()
|
738
|
629
|
{
|
739
|
630
|
if (encoderPosition != 0)
|
|
@@ -752,12 +643,7 @@ static void lcd_move_e()
|
752
|
643
|
{
|
753
|
644
|
lcd_implementation_drawedit(PSTR("Extruder"), ftostr31(current_position[E_AXIS]));
|
754
|
645
|
}
|
755
|
|
- if (LCD_CLICKED)
|
756
|
|
- {
|
757
|
|
- lcd_quick_feedback();
|
758
|
|
- currentMenu = lcd_move_menu_axis;
|
759
|
|
- encoderPosition = 0;
|
760
|
|
- }
|
|
646
|
+ if (LCD_CLICKED) lcd_goto_menu(lcd_move_menu_axis);
|
761
|
647
|
}
|
762
|
648
|
|
763
|
649
|
static void lcd_move_menu_axis()
|
|
@@ -951,12 +837,7 @@ static void lcd_set_contrast()
|
951
|
837
|
{
|
952
|
838
|
lcd_implementation_drawedit(PSTR(MSG_CONTRAST), itostr2(lcd_contrast));
|
953
|
839
|
}
|
954
|
|
- if (LCD_CLICKED)
|
955
|
|
- {
|
956
|
|
- lcd_quick_feedback();
|
957
|
|
- currentMenu = lcd_control_menu;
|
958
|
|
- encoderPosition = 0;
|
959
|
|
- }
|
|
840
|
+ if (LCD_CLICKED) lcd_goto_menu(lcd_control_menu);
|
960
|
841
|
}
|
961
|
842
|
#endif
|
962
|
843
|
|
|
@@ -979,7 +860,8 @@ static void lcd_control_retract_menu()
|
979
|
860
|
MENU_ITEM_EDIT(float3, MSG_CONTROL_RETRACT_RECOVERF, &retract_recover_feedrate, 1, 999);
|
980
|
861
|
END_MENU();
|
981
|
862
|
}
|
982
|
|
-#endif
|
|
863
|
+
|
|
864
|
+#endif //FWRETRACT
|
983
|
865
|
|
984
|
866
|
#if SDCARDDETECT == -1
|
985
|
867
|
static void lcd_sd_refresh()
|
|
@@ -1052,27 +934,12 @@ void lcd_sdcard_menu()
|
1052
|
934
|
if (LCD_CLICKED) \
|
1053
|
935
|
{ \
|
1054
|
936
|
*((_type*)editValue) = ((_type)((int32_t)encoderPosition + minEditValue)) / scale; \
|
1055
|
|
- lcd_quick_feedback(); \
|
1056
|
|
- currentMenu = prevMenu; \
|
1057
|
|
- encoderPosition = prevEncoderPosition; \
|
|
937
|
+ lcd_goto_menu(prevMenu, prevEncoderPosition); \
|
1058
|
938
|
} \
|
1059
|
939
|
} \
|
1060
|
|
- void menu_edit_callback_ ## _name () \
|
1061
|
|
- { \
|
1062
|
|
- if ((int32_t)encoderPosition < 0) \
|
1063
|
|
- encoderPosition = 0; \
|
1064
|
|
- if ((int32_t)encoderPosition > maxEditValue) \
|
1065
|
|
- encoderPosition = maxEditValue; \
|
1066
|
|
- if (lcdDrawUpdate) \
|
1067
|
|
- lcd_implementation_drawedit(editLabel, _strFunc(((_type)((int32_t)encoderPosition + minEditValue)) / scale)); \
|
1068
|
|
- if (LCD_CLICKED) \
|
1069
|
|
- { \
|
1070
|
|
- *((_type*)editValue) = ((_type)((int32_t)encoderPosition + minEditValue)) / scale; \
|
1071
|
|
- lcd_quick_feedback(); \
|
1072
|
|
- currentMenu = prevMenu; \
|
1073
|
|
- encoderPosition = prevEncoderPosition; \
|
1074
|
|
- (*callbackFunc)();\
|
1075
|
|
- } \
|
|
940
|
+ void menu_edit_callback_ ## _name () { \
|
|
941
|
+ menu_edit_ ## _name (); \
|
|
942
|
+ if (LCD_CLICKED) (*callbackFunc)(); \
|
1076
|
943
|
} \
|
1077
|
944
|
static void menu_action_setting_edit_ ## _name (const char* pstr, _type* ptr, _type minValue, _type maxValue) \
|
1078
|
945
|
{ \
|
|
@@ -1157,24 +1024,10 @@ static void lcd_quick_feedback()
|
1157
|
1024
|
}
|
1158
|
1025
|
|
1159
|
1026
|
/** Menu action functions **/
|
1160
|
|
-static void menu_action_back(menuFunc_t data)
|
1161
|
|
-{
|
1162
|
|
- currentMenu = data;
|
1163
|
|
- encoderPosition = 0;
|
1164
|
|
-}
|
1165
|
|
-static void menu_action_submenu(menuFunc_t data)
|
1166
|
|
-{
|
1167
|
|
- currentMenu = data;
|
1168
|
|
- encoderPosition = 0;
|
1169
|
|
-}
|
1170
|
|
-static void menu_action_gcode(const char* pgcode)
|
1171
|
|
-{
|
1172
|
|
- enquecommand_P(pgcode);
|
1173
|
|
-}
|
1174
|
|
-static void menu_action_function(menuFunc_t data)
|
1175
|
|
-{
|
1176
|
|
- (*data)();
|
1177
|
|
-}
|
|
1027
|
+static void menu_action_back(menuFunc_t data) { lcd_goto_menu(data); }
|
|
1028
|
+static void menu_action_submenu(menuFunc_t data) { lcd_goto_menu(data); }
|
|
1029
|
+static void menu_action_gcode(const char* pgcode) { enquecommand_P(pgcode); }
|
|
1030
|
+static void menu_action_function(menuFunc_t data) { (*data)(); }
|
1178
|
1031
|
static void menu_action_sdfile(const char* filename, char* longFilename)
|
1179
|
1032
|
{
|
1180
|
1033
|
char cmd[30];
|
|
@@ -1459,10 +1312,8 @@ void lcd_buttons_update()
|
1459
|
1312
|
|
1460
|
1313
|
//manage encoder rotation
|
1461
|
1314
|
uint8_t enc=0;
|
1462
|
|
- if(buttons&EN_A)
|
1463
|
|
- enc|=(1<<0);
|
1464
|
|
- if(buttons&EN_B)
|
1465
|
|
- enc|=(1<<1);
|
|
1315
|
+ if (buttons & EN_A) enc |= B01;
|
|
1316
|
+ if (buttons & EN_B) enc |= B10;
|
1466
|
1317
|
if(enc != lastEncoderBits)
|
1467
|
1318
|
{
|
1468
|
1319
|
switch(enc)
|
|
@@ -1611,6 +1462,7 @@ char *itostr31(const int &xx)
|
1611
|
1462
|
return conv;
|
1612
|
1463
|
}
|
1613
|
1464
|
|
|
1465
|
+// Convert int to rj string with 123 or -12 format
|
1614
|
1466
|
char *itostr3(const int &x)
|
1615
|
1467
|
{
|
1616
|
1468
|
int xx = x;
|
|
@@ -1653,47 +1505,25 @@ char *itostr3left(const int &xx)
|
1653
|
1505
|
return conv;
|
1654
|
1506
|
}
|
1655
|
1507
|
|
1656
|
|
-char *itostr4(const int &xx)
|
1657
|
|
-{
|
1658
|
|
- if (xx >= 1000)
|
1659
|
|
- conv[0]=(xx/1000)%10+'0';
|
1660
|
|
- else
|
1661
|
|
- conv[0]=' ';
|
1662
|
|
- if (xx >= 100)
|
1663
|
|
- conv[1]=(xx/100)%10+'0';
|
1664
|
|
- else
|
1665
|
|
- conv[1]=' ';
|
1666
|
|
- if (xx >= 10)
|
1667
|
|
- conv[2]=(xx/10)%10+'0';
|
1668
|
|
- else
|
1669
|
|
- conv[2]=' ';
|
1670
|
|
- conv[3]=(xx)%10+'0';
|
1671
|
|
- conv[4]=0;
|
|
1508
|
+// Convert int to rj string with 1234 format
|
|
1509
|
+char *itostr4(const int &xx) {
|
|
1510
|
+ conv[0] = xx >= 1000 ? (xx / 1000) % 10 + '0' : ' ';
|
|
1511
|
+ conv[1] = xx >= 100 ? (xx / 100) % 10 + '0' : ' ';
|
|
1512
|
+ conv[2] = xx >= 10 ? (xx / 10) % 10 + '0' : ' ';
|
|
1513
|
+ conv[3] = xx % 10 + '0';
|
|
1514
|
+ conv[4] = 0;
|
1672
|
1515
|
return conv;
|
1673
|
1516
|
}
|
1674
|
1517
|
|
1675
|
|
-// convert float to string with 12345 format
|
1676
|
|
-char *ftostr5(const float &x)
|
1677
|
|
-{
|
1678
|
|
- long xx=abs(x);
|
1679
|
|
- if (xx >= 10000)
|
1680
|
|
- conv[0]=(xx/10000)%10+'0';
|
1681
|
|
- else
|
1682
|
|
- conv[0]=' ';
|
1683
|
|
- if (xx >= 1000)
|
1684
|
|
- conv[1]=(xx/1000)%10+'0';
|
1685
|
|
- else
|
1686
|
|
- conv[1]=' ';
|
1687
|
|
- if (xx >= 100)
|
1688
|
|
- conv[2]=(xx/100)%10+'0';
|
1689
|
|
- else
|
1690
|
|
- conv[2]=' ';
|
1691
|
|
- if (xx >= 10)
|
1692
|
|
- conv[3]=(xx/10)%10+'0';
|
1693
|
|
- else
|
1694
|
|
- conv[3]=' ';
|
1695
|
|
- conv[4]=(xx)%10+'0';
|
1696
|
|
- conv[5]=0;
|
|
1518
|
+// convert float to rj string with 12345 format
|
|
1519
|
+char *ftostr5(const float &x) {
|
|
1520
|
+ long xx = abs(x);
|
|
1521
|
+ conv[0] = xx >= 10000 ? (xx / 10000) % 10 + '0' : ' ';
|
|
1522
|
+ conv[1] = xx >= 1000 ? (xx / 1000) % 10 + '0' : ' ';
|
|
1523
|
+ conv[2] = xx >= 100 ? (xx / 100) % 10 + '0' : ' ';
|
|
1524
|
+ conv[3] = xx >= 10 ? (xx / 10) % 10 + '0' : ' ';
|
|
1525
|
+ conv[4] = xx % 10 + '0';
|
|
1526
|
+ conv[5] = 0;
|
1697
|
1527
|
return conv;
|
1698
|
1528
|
}
|
1699
|
1529
|
|