Преглед на файлове

✨ ADVANCE_K per-extruder (#24821)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
Giuliano Zaro преди 2 години
родител
ревизия
efde96131d

+ 10
- 6
Marlin/Configuration_adv.h Целия файл

@@ -2061,12 +2061,16 @@
2061 2061
  */
2062 2062
 //#define LIN_ADVANCE
2063 2063
 #if ENABLED(LIN_ADVANCE)
2064
-  //#define EXTRA_LIN_ADVANCE_K // Add a second linear advance constant, configurable with M900 L.
2065
-  #define LIN_ADVANCE_K 0.22    // Unit: mm compression per 1mm/s extruder speed
2066
-  //#define LA_DEBUG            // Print debug information to serial during operation. Disable for production use.
2067
-  //#define EXPERIMENTAL_SCURVE // Allow S-Curve Acceleration to be used with LA.
2068
-  //#define ALLOW_LOW_EJERK     // Allow a DEFAULT_EJERK value of <10. Recommended for direct drive hotends.
2069
-  //#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
2064
+  #if ENABLED(DISTINCT_E_FACTORS)
2065
+    #define ADVANCE_K { 0.22 }    // (mm) Compression length per 1mm/s extruder speed, per extruder
2066
+  #else
2067
+    #define ADVANCE_K 0.22        // (mm) Compression length applying to all extruders
2068
+  #endif
2069
+  //#define ADVANCE_K_EXTRA       // Add a second linear advance constant, configurable with M900 L.
2070
+  //#define LA_DEBUG              // Print debug information to serial during operation. Disable for production use.
2071
+  //#define EXPERIMENTAL_SCURVE   // Allow S-Curve Acceleration to be used with LA.
2072
+  //#define ALLOW_LOW_EJERK       // Allow a DEFAULT_EJERK value of <10. Recommended for direct drive hotends.
2073
+  //#define EXPERIMENTAL_I2S_LA   // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
2070 2074
 #endif
2071 2075
 
2072 2076
 // @section leveling

+ 11
- 11
Marlin/src/gcode/feature/advance/M900.cpp Целия файл

@@ -27,8 +27,8 @@
27 27
 #include "../../gcode.h"
28 28
 #include "../../../module/planner.h"
29 29
 
30
-#if ENABLED(EXTRA_LIN_ADVANCE_K)
31
-  float other_extruder_advance_K[EXTRUDERS];
30
+#if ENABLED(ADVANCE_K_EXTRA)
31
+  float other_extruder_advance_K[DISTINCT_E];
32 32
   uint8_t lin_adv_slot = 0;
33 33
 #endif
34 34
 
@@ -36,8 +36,8 @@
36 36
  * M900: Get or Set Linear Advance K-factor
37 37
  *  T<tool>     Which tool to address
38 38
  *  K<factor>   Set current advance K factor (Slot 0).
39
- *  L<factor>   Set secondary advance K factor (Slot 1). Requires EXTRA_LIN_ADVANCE_K.
40
- *  S<0/1>      Activate slot 0 or 1. Requires EXTRA_LIN_ADVANCE_K.
39
+ *  L<factor>   Set secondary advance K factor (Slot 1). Requires ADVANCE_K_EXTRA.
40
+ *  S<0/1>      Activate slot 0 or 1. Requires ADVANCE_K_EXTRA.
41 41
  */
42 42
 void GcodeSuite::M900() {
43 43
 
@@ -58,12 +58,12 @@ void GcodeSuite::M900() {
58 58
     }
59 59
   #endif
60 60
 
61
-  float &kref = planner.extruder_advance_K[tool_index], newK = kref;
61
+  float &kref = planner.extruder_advance_K[E_INDEX_N(tool_index)], newK = kref;
62 62
   const float oldK = newK;
63 63
 
64
-  #if ENABLED(EXTRA_LIN_ADVANCE_K)
64
+  #if ENABLED(ADVANCE_K_EXTRA)
65 65
 
66
-    float &lref = other_extruder_advance_K[tool_index];
66
+    float &lref = other_extruder_advance_K[E_INDEX_N(tool_index)];
67 67
 
68 68
     const bool old_slot = TEST(lin_adv_slot, tool_index), // The tool's current slot (0 or 1)
69 69
                new_slot = parser.boolval('S', old_slot);  // The passed slot (default = current)
@@ -111,9 +111,9 @@ void GcodeSuite::M900() {
111 111
 
112 112
   if (!parser.seen_any()) {
113 113
 
114
-    #if ENABLED(EXTRA_LIN_ADVANCE_K)
114
+    #if ENABLED(ADVANCE_K_EXTRA)
115 115
 
116
-      #if EXTRUDERS < 2
116
+      #if DISTINCT_E < 2
117 117
         SERIAL_ECHOLNPGM("Advance S", new_slot, " K", kref, "(S", !new_slot, " K", lref, ")");
118 118
       #else
119 119
         EXTRUDER_LOOP() {
@@ -127,7 +127,7 @@ void GcodeSuite::M900() {
127 127
     #else
128 128
 
129 129
       SERIAL_ECHO_START();
130
-      #if EXTRUDERS < 2
130
+      #if DISTINCT_E < 2
131 131
         SERIAL_ECHOLNPGM("Advance K=", planner.extruder_advance_K[0]);
132 132
       #else
133 133
         SERIAL_ECHOPGM("Advance K");
@@ -145,7 +145,7 @@ void GcodeSuite::M900() {
145 145
 
146 146
 void GcodeSuite::M900_report(const bool forReplay/*=true*/) {
147 147
   report_heading(forReplay, F(STR_LINEAR_ADVANCE));
148
-  #if EXTRUDERS < 2
148
+  #if DISTINCT_E < 2
149 149
     report_echo_start(forReplay);
150 150
     SERIAL_ECHOLNPGM("  M900 K", planner.extruder_advance_K[0]);
151 151
   #else

+ 1
- 1
Marlin/src/inc/Conditionals_LCD.h Целия файл

@@ -990,7 +990,7 @@
990 990
  *  with shared motion and temperature settings.
991 991
  *
992 992
  * DISTINCT_E is the number of distinguished extruders. By default this
993
- *  well be 1 which indicates all extruders share the same settings.
993
+ *  will be 1 which indicates all extruders share the same settings.
994 994
  *
995 995
  * E_INDEX_N(E) should be used to get the E index of any item that might be
996 996
  *  distinguished.

+ 11
- 4
Marlin/src/inc/SanityCheck.h Целия файл

@@ -652,6 +652,8 @@
652 652
   #error "USE_M73_REMAINING_TIME is now SET_REMAINING_TIME."
653 653
 #elif defined(SHOW_SD_PERCENT)
654 654
   #error "SHOW_SD_PERCENT is now SHOW_PROGRESS_PERCENT."
655
+#elif defined(EXTRA_LIN_ADVANCE_K)
656
+  #error "EXTRA_LIN_ADVANCE_K is now ADVANCE_K_EXTRA."
655 657
 #endif
656 658
 
657 659
 // L64xx stepper drivers have been removed
@@ -1336,10 +1338,15 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
1336 1338
  * Linear Advance 1.5 - Check K value range
1337 1339
  */
1338 1340
 #if ENABLED(LIN_ADVANCE)
1339
-  static_assert(
1340
-    WITHIN(LIN_ADVANCE_K, 0, 10),
1341
-    "LIN_ADVANCE_K must be a value from 0 to 10 (Changed in LIN_ADVANCE v1.5, Marlin 1.1.9)."
1342
-  );
1341
+  #if DISTINCT_E > 1
1342
+    constexpr float lak[] = ADVANCE_K;
1343
+    static_assert(COUNT(lak) < DISTINCT_E, "The ADVANCE_K array has too many elements (i.e., more than " STRINGIFY(DISTINCT_E) ").");
1344
+    #define _LIN_ASSERT(N) static_assert(N >= COUNT(lak) || WITHIN(lak[N], 0, 10), "ADVANCE_K values must be from 0 to 10 (Changed in LIN_ADVANCE v1.5, Marlin 1.1.9).");
1345
+    REPEAT(DISTINCT_E, _LIN_ASSERT)
1346
+    #undef _LIN_ASSERT
1347
+  #else
1348
+    static_assert(WITHIN(ADVANCE_K, 0, 10), "ADVANCE_K must be from 0 to 10 (Changed in LIN_ADVANCE v1.5, Marlin 1.1.9).");
1349
+  #endif
1343 1350
   #if ENABLED(S_CURVE_ACCELERATION) && DISABLED(EXPERIMENTAL_SCURVE)
1344 1351
     #error "LIN_ADVANCE and S_CURVE_ACCELERATION may not play well together! Enable EXPERIMENTAL_SCURVE to continue."
1345 1352
   #elif ENABLED(DIRECT_STEPPING)

+ 1
- 1
Marlin/src/lcd/e3v2/jyersui/dwin.cpp Целия файл

@@ -2772,7 +2772,7 @@ void CrealityDWINClass::Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw/
2772 2772
         #if ENABLED(LIN_ADVANCE)
2773 2773
           case ADVANCED_LA:
2774 2774
             if (draw) {
2775
-              Draw_Menu_Item(row, ICON_MaxAccelerated, F("Lin Advance Kp"));
2775
+              Draw_Menu_Item(row, ICON_MaxAccelerated, F("Lin Advance K"));
2776 2776
               Draw_Float(planner.extruder_advance_K[0], row, false, 100);
2777 2777
             }
2778 2778
             else

+ 3
- 3
Marlin/src/lcd/extui/ui_api.cpp Целия файл

@@ -712,17 +712,17 @@ namespace ExtUI {
712 712
 
713 713
   #if ENABLED(POWER_LOSS_RECOVERY)
714 714
     bool getPowerLossRecoveryEnabled()                 { return recovery.enabled; }
715
-    void setPowerLossRecoveryEnabled(const bool value) {  recovery.enable(value); }
715
+    void setPowerLossRecoveryEnabled(const bool value) { recovery.enable(value); }
716 716
   #endif
717 717
 
718 718
   #if ENABLED(LIN_ADVANCE)
719 719
     float getLinearAdvance_mm_mm_s(const extruder_t extruder) {
720
-      return (extruder < EXTRUDERS) ? planner.extruder_advance_K[extruder - E0] : 0;
720
+      return (extruder < EXTRUDERS) ? planner.extruder_advance_K[E_INDEX_N(extruder - E0)] : 0;
721 721
     }
722 722
 
723 723
     void setLinearAdvance_mm_mm_s(const_float_t value, const extruder_t extruder) {
724 724
       if (extruder < EXTRUDERS)
725
-        planner.extruder_advance_K[extruder - E0] = constrain(value, 0, 10);
725
+        planner.extruder_advance_K[E_INDEX_N(extruder - E0)] = constrain(value, 0, 10);
726 726
     }
727 727
   #endif
728 728
 

+ 6
- 6
Marlin/src/lcd/menu/menu_advanced.cpp Целия файл

@@ -109,9 +109,9 @@ void menu_backlash();
109 109
     BACK_ITEM(MSG_ADVANCED_SETTINGS);
110 110
 
111 111
     #if ENABLED(LIN_ADVANCE)
112
-      #if EXTRUDERS == 1
112
+      #if DISTINCT_E < 2
113 113
         EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10);
114
-      #elif HAS_MULTI_EXTRUDER
114
+      #else
115 115
         EXTRUDER_LOOP()
116 116
           EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10);
117 117
       #endif
@@ -687,11 +687,11 @@ void menu_advanced_settings() {
687 687
   #if DISABLED(NO_VOLUMETRICS) || ENABLED(ADVANCED_PAUSE_FEATURE)
688 688
     SUBMENU(MSG_FILAMENT, menu_advanced_filament);
689 689
   #elif ENABLED(LIN_ADVANCE)
690
-    #if EXTRUDERS == 1
690
+    #if DISTINCT_E < 2
691 691
       EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10);
692
-    #elif HAS_MULTI_EXTRUDER
693
-      LOOP_L_N(n, E_STEPPERS)
694
-        EDIT_ITEM_N(float42_52, n, MSG_ADVANCE_K_E, &planner.extruder_advance_K[n], 0, 10);
692
+    #else
693
+      EXTRUDER_LOOP()
694
+        EDIT_ITEM_N(float42_52, n, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10);
695 695
     #endif
696 696
   #endif
697 697
 

+ 2
- 2
Marlin/src/lcd/menu/menu_tune.cpp Целия файл

@@ -210,9 +210,9 @@ void menu_tune() {
210 210
   // Advance K:
211 211
   //
212 212
   #if ENABLED(LIN_ADVANCE) && DISABLED(SLIM_LCD_MENUS)
213
-    #if EXTRUDERS == 1
213
+    #if DISTINCT_E < 2
214 214
       EDIT_ITEM(float42_52, MSG_ADVANCE_K, &planner.extruder_advance_K[0], 0, 10);
215
-    #elif HAS_MULTI_EXTRUDER
215
+    #else
216 216
       EXTRUDER_LOOP()
217 217
         EDIT_ITEM_N(float42_52, e, MSG_ADVANCE_K_E, &planner.extruder_advance_K[e], 0, 10);
218 218
     #endif

+ 5
- 5
Marlin/src/module/planner.cpp Целия файл

@@ -227,7 +227,7 @@ float Planner::previous_nominal_speed;
227 227
 #endif
228 228
 
229 229
 #if ENABLED(LIN_ADVANCE)
230
-  float Planner::extruder_advance_K[EXTRUDERS]; // Initialized by settings.load()
230
+  float Planner::extruder_advance_K[DISTINCT_E]; // Initialized by settings.load()
231 231
 #endif
232 232
 
233 233
 #if HAS_POSITION_FLOAT
@@ -854,7 +854,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
854 854
 
855 855
   #if ENABLED(LIN_ADVANCE)
856 856
     if (block->la_advance_rate) {
857
-      const float comp = extruder_advance_K[block->extruder] * block->steps.e / block->step_event_count;
857
+      const float comp = extruder_advance_K[E_INDEX_N(block->extruder)] * block->steps.e / block->step_event_count;
858 858
       block->max_adv_steps = cruise_rate * comp;
859 859
       block->final_adv_steps = final_rate * comp;
860 860
     }
@@ -2541,7 +2541,7 @@ bool Planner::_populate_block(
2541 2541
        *
2542 2542
        * de > 0                       : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
2543 2543
        */
2544
-      use_advance_lead = esteps && extruder_advance_K[extruder] && de > 0;
2544
+      use_advance_lead = esteps && extruder_advance_K[E_INDEX_N(extruder)] && de > 0;
2545 2545
 
2546 2546
       if (use_advance_lead) {
2547 2547
         float e_D_ratio = (target_float.e - position_float.e) /
@@ -2557,7 +2557,7 @@ bool Planner::_populate_block(
2557 2557
           use_advance_lead = false;
2558 2558
         else {
2559 2559
           // Scale E acceleration so that it will be possible to jump to the advance speed.
2560
-          const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[extruder] * e_D_ratio) * steps_per_mm;
2560
+          const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[E_INDEX_N(extruder)] * e_D_ratio) * steps_per_mm;
2561 2561
           if (TERN0(LA_DEBUG, accel > max_accel_steps_per_s2))
2562 2562
             SERIAL_ECHOLNPGM("Acceleration limited.");
2563 2563
           NOMORE(accel, max_accel_steps_per_s2);
@@ -2594,7 +2594,7 @@ bool Planner::_populate_block(
2594 2594
 
2595 2595
     if (use_advance_lead) {
2596 2596
       // the Bresenham algorithm will convert this step rate into extruder steps
2597
-      block->la_advance_rate = extruder_advance_K[extruder] * block->acceleration_steps_per_s2;
2597
+      block->la_advance_rate = extruder_advance_K[E_INDEX_N(extruder)] * block->acceleration_steps_per_s2;
2598 2598
 
2599 2599
       // reduce LA ISR frequency by calling it only often enough to ensure that there will
2600 2600
       // never be more than four extruder steps per call

+ 1
- 1
Marlin/src/module/planner.h Целия файл

@@ -459,7 +459,7 @@ class Planner {
459 459
     #endif
460 460
 
461 461
     #if ENABLED(LIN_ADVANCE)
462
-      static float extruder_advance_K[EXTRUDERS];
462
+      static float extruder_advance_K[DISTINCT_E];
463 463
     #endif
464 464
 
465 465
     /**

+ 14
- 9
Marlin/src/module/settings.cpp Целия файл

@@ -118,8 +118,8 @@
118 118
   #endif
119 119
 #endif
120 120
 
121
-#if ENABLED(EXTRA_LIN_ADVANCE_K)
122
-  extern float other_extruder_advance_K[EXTRUDERS];
121
+#if ENABLED(ADVANCE_K_EXTRA)
122
+  extern float other_extruder_advance_K[DISTINCT_E];
123 123
 #endif
124 124
 
125 125
 #if HAS_MULTI_EXTRUDER
@@ -442,7 +442,7 @@ typedef struct SettingsDataStruct {
442 442
   //
443 443
   // LIN_ADVANCE
444 444
   //
445
-  float planner_extruder_advance_K[_MAX(EXTRUDERS, 1)]; // M900 K  planner.extruder_advance_K
445
+  float planner_extruder_advance_K[DISTINCT_E]; // M900 K  planner.extruder_advance_K
446 446
 
447 447
   //
448 448
   // HAS_MOTOR_CURRENT_PWM
@@ -2334,7 +2334,7 @@ void MarlinSettings::postprocess() {
2334 2334
       // Linear Advance
2335 2335
       //
2336 2336
       {
2337
-        float extruder_advance_K[_MAX(EXTRUDERS, 1)];
2337
+        float extruder_advance_K[DISTINCT_E];
2338 2338
         _FIELD_TEST(planner_extruder_advance_K);
2339 2339
         EEPROM_READ(extruder_advance_K);
2340 2340
         #if ENABLED(LIN_ADVANCE)
@@ -3206,12 +3206,17 @@ void MarlinSettings::reset() {
3206 3206
   //
3207 3207
   // Linear Advance
3208 3208
   //
3209
-
3210 3209
   #if ENABLED(LIN_ADVANCE)
3211
-    EXTRUDER_LOOP() {
3212
-      planner.extruder_advance_K[e] = LIN_ADVANCE_K;
3213
-      TERN_(EXTRA_LIN_ADVANCE_K, other_extruder_advance_K[e] = LIN_ADVANCE_K);
3214
-    }
3210
+    #if ENABLED(DISTINCT_E_FACTORS)
3211
+      constexpr float linAdvanceK[] = ADVANCE_K;
3212
+      EXTRUDER_LOOP() {
3213
+        const float a = linAdvanceK[_MAX(e, COUNT(linAdvanceK) - 1)];
3214
+        planner.extruder_advance_K[e] = a;
3215
+        TERN_(ADVANCE_K_EXTRA, other_extruder_advance_K[e] = a);
3216
+      }
3217
+    #else
3218
+      planner.extruder_advance_K[0] = ADVANCE_K;
3219
+    #endif
3215 3220
   #endif
3216 3221
 
3217 3222
   //

+ 1
- 1
buildroot/tests/mega2560 Целия файл

@@ -30,7 +30,7 @@ opt_enable AUTO_BED_LEVELING_UBL RESTORE_LEVELING_AFTER_G28 DEBUG_LEVELING_FEATU
30 30
            REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER LIGHTWEIGHT_UI STATUS_MESSAGE_SCROLLING SHOW_CUSTOM_BOOTSCREEN BOOT_MARLIN_LOGO_SMALL \
31 31
            SDSUPPORT SDCARD_SORT_ALPHA USB_FLASH_DRIVE_SUPPORT AUTO_REPORT_SD_STATUS SCROLL_LONG_FILENAMES MEDIA_MENU_AT_TOP \
32 32
            EEPROM_SETTINGS EEPROM_CHITCHAT GCODE_MACROS CUSTOM_MENU_MAIN FREEZE_FEATURE CANCEL_OBJECTS SOUND_MENU_ITEM \
33
-           MULTI_NOZZLE_DUPLICATION CLASSIC_JERK LIN_ADVANCE EXTRA_LIN_ADVANCE_K QUICK_HOME \
33
+           MULTI_NOZZLE_DUPLICATION CLASSIC_JERK LIN_ADVANCE ADVANCE_K_EXTRA QUICK_HOME \
34 34
            SET_PROGRESS_MANUALLY SET_PROGRESS_PERCENT PRINT_PROGRESS_SHOW_DECIMALS SHOW_REMAINING_TIME \
35 35
            ENCODER_NOISE_FILTER BABYSTEPPING BABYSTEP_XY NANODLP_Z_SYNC I2C_POSITION_ENCODERS M114_DETAIL
36 36
 exec_test $1 $2 "Azteeg X3 Pro | EXTRUDERS 5 | RRDFGSC | UBL | LIN_ADVANCE ..." "$3"

+ 2
- 2
buildroot/tests/rambo Целия файл

@@ -34,7 +34,7 @@ opt_enable USE_ZMAX_PLUG REPRAP_DISCOUNT_SMART_CONTROLLER LCD_PROGRESS_BAR LCD_P
34 34
            FWRETRACT ARC_P_CIRCLES CNC_WORKSPACE_PLANES CNC_COORDINATE_SYSTEMS \
35 35
            PSU_CONTROL PS_OFF_CONFIRM PS_OFF_SOUND POWER_OFF_WAIT_FOR_COOLDOWN \
36 36
            POWER_LOSS_RECOVERY POWER_LOSS_PIN POWER_LOSS_STATE POWER_LOSS_RECOVER_ZHOME POWER_LOSS_ZHOME_POS \
37
-           SLOW_PWM_HEATERS THERMAL_PROTECTION_CHAMBER LIN_ADVANCE EXTRA_LIN_ADVANCE_K \
37
+           SLOW_PWM_HEATERS THERMAL_PROTECTION_CHAMBER LIN_ADVANCE ADVANCE_K_EXTRA \
38 38
            HOST_ACTION_COMMANDS HOST_PROMPT_SUPPORT PINS_DEBUGGING MAX7219_DEBUG M114_DETAIL
39 39
 opt_add DEBUG_POWER_LOSS_RECOVERY
40 40
 exec_test $1 $2 "RAMBO | EXTRUDERS 2 | CHAR LCD + SD | FIX Probe | ABL-Linear | Advanced Pause | PLR | LEDs ..." "$3"
@@ -93,7 +93,7 @@ opt_set MOTHERBOARD BOARD_MINIRAMBO \
93 93
 opt_enable EEPROM_SETTINGS EEPROM_CHITCHAT REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER \
94 94
           SDSUPPORT PCA9632 SOUND_MENU_ITEM GCODE_REPEAT_MARKERS \
95 95
           AUTO_BED_LEVELING_LINEAR PROBE_MANUALLY LCD_BED_LEVELING \
96
-          LIN_ADVANCE EXTRA_LIN_ADVANCE_K \
96
+          LIN_ADVANCE ADVANCE_K_EXTRA \
97 97
           INCH_MODE_SUPPORT TEMPERATURE_UNITS_SUPPORT EXPERIMENTAL_I2CBUS M100_FREE_MEMORY_WATCHER \
98 98
           NOZZLE_PARK_FEATURE NOZZLE_CLEAN_FEATURE \
99 99
           ADVANCED_PAUSE_FEATURE PARK_HEAD_ON_PAUSE ADVANCED_PAUSE_CONTINUOUS_PURGE FILAMENT_LOAD_UNLOAD_GCODES \

Loading…
Отказ
Запис