Рефакторинг плохого дизайна?
Может ли кто-нибудь помочь мне с рефакторингом старого очень сложного кода?
В оригинале использовалась только установка, а все остальное было в одном ISR. Но так как он был все больше и больше, код довольно медленный и иногда довольно случайный, даже эмулятор ПК не сильно помогал (вероятно, некоторые длительные операции, такие как печать, иногда прерываются следующим прерыванием в зависимости от аналоговых значений, кнопок и т. д.).
Есть ли какое-нибудь руководство по поддержке такого проекта или есть только выход начать все сначала?
Поскольку существует несколько важных правил, также потребуется очередь событий, но не уверен, есть ли какой-либо шаблон или шаблон, с которого можно начать в случае перезапуска. A-Star 32U4 Micro имеет флэш-память < 32 КБ, и код пока трудно вместить (есть также большие шрифты).
Пример кода распаковки шрифта, но надеюсь, что с этой частью все в порядке:
void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c,
uint16_t color, uint16_t bg, uint8_t size) {
...
uint8_t blockSize = 0;
if(packed) blockSize = pgm_read_byte(&bitmap[bo++]); // для первого символа выбрать только 1-й битовый массив
BitStreamsReader bitStream(&bitmap[bo++], packed?blockSize:0); // указатель, размер блока (если упаковано)
BitXorBuffer rowXorBuf(w); // Буфер XOR строк (с каждой строкой выполняется XOR)
bool chk;
for(yy=0; yy<h; yy++) {
rowXorBuf.rewind();
for(xx=0; xx<w; xx++) {
chk = bitStream; // чтение 1b
if(packed) chk = rowXorBuf.xorMove(chk); // xor с буфером XOR
if(chk) {
if(size == 1) {
writePixel(x+xo+xx, y+yo+yy, color);
} else {
writeFillRect(x+(xo16+xx)*size, y+(yo16+yy)*size,
size, size, color);
}
}
}
}
...
class BitStreamsReader {
public:
BitStreamsReader(uint8_t *arr, unsigned int maxSize) {
blockSize = maxSize;
bitMask = 0x80;
bytePtr = arr;
zerosCount = 0;
remainingBits = 0;
markBitInit();
}
operator bool() {
if(!blockSize) { // распакованный поток
bool retVal = !!(pgm_read_byte(bytePtr) & bitMask);
next();
return retVal;
} else if(remainingBits) {
bool retVal = !!(pgm_read_byte(bytePtr) & bitMask);
next();
if(!--remainingBits) {
markBitInit();
}
return retVal;
} else if(zerosCount) {
if(!--zerosCount) {
markBitInit();
}
return 0;
}
return 0; //бросать "??";
}
private:
void markBitInit() {
if(blockSize) {
if(pgm_read_byte(bytePtr) & bitMask) {
remainingBits = blockSize;
} else {
zerosCount = blockSize;
}
next();
}
}
void next () {
if(bitMask == 0x01) {
bitMask = 0x80;
bytePtr++;
} else {
bitMask >>= 1;
}
};
uint8_t *bytePtr, bitMask, zerosCount, remainingBits, blockSize;
};
class BitXorBuffer {
public:
BitXorBuffer(int bufSize) {
bufSize = bufSize/8+!!(bufSize&7);
bitBuf = new uint8_t[bufSize];
memset(bitBuf, 0, bufSize);
rewind();
}
~BitXorBuffer() {
delete[] bitBuf;
}
void rewind() {
bitMask = 128;
bytePtr = bitBuf;
}
bool xorMove(bool by) {
if(bitMask & *bytePtr) by ^= true;
*bytePtr |= bitMask;
if(!by) {
*bytePtr ^= bitMask;
}
nextBit();
return by;
}
private:
void nextBit() {
if(bitMask > 1) {
bitMask >>= 1; // перемещаем бит в буфер XOR
} else {
bitMask = 128;
bytePtr++;
}
}
uint8_t *bytePtr, *bitBuf, bitMask;
};
@Tom, 👍-1
Обсуждение1 ответ
Лучший ответ:
Я постараюсь как можно лучше ответить на ваш вопрос, хотя он и довольно общий.
Насколько мне известно, не существует ОДНОГО шаблона или шаблона проектирования, подходящего для всех случаев. Но в зависимости от фактических функций вашего кода вы можете пойти разными путями, используя библиотеки (которые в этом смысле чем-то похожи на шаблоны).
Вы можете упростить многозадачность, используя такие библиотеки, как FreeRTOS. Вы даже можете найти совместимую версию SafeRTOS, брата FreeRTOS, в которой больше внимания уделяется безопасности. Хотя вся система RTOS может вам не подойти, и ваш код должен быть в основном переписан/переработан.
В зависимости от того, какие ISR используются и для чего они предназначены, вам может действительно понадобиться использовать прерывания, чтобы устройство реагировало немедленно. В этом случае у вас в основном нет выбора, кроме рефакторинга всего кода, как описано в комментариях (перемещение как можно большего количества кода в основной цикл, что значительно уменьшает ISR). Для этого вот несколько общих советов:
- Сначала нарисуйте график работы устройств, как это должно быть. Определите части кода, которые имеют общую функцию, но разделите их на блоки, нарисуйте стрелки между блоками, чтобы показать, как они связаны логически. Затем определите, какие части действительно критичны по времени и каковы временные ограничения (например, вам нужно, чтобы устройство реагировало на событие в течение нескольких тактов или нормально, если реакция приходит на несколько секунд позже?). Отметьте, как часто задействованные события происходят в среднем и максимально.
- Теперь посмотрите непосредственно на блоки кода. Какие из них нуждаются в прерываниях для работы и что с ними происходит, если прерывания отключаются во время их работы (поскольку, как вы уже определили, повторная активация прерываний внутри ISR может стать очень грязной). Например, если прерывания отключены, последовательные отпечатки не будут отправляться напрямую (поскольку это зависит от прерываний). Вместо этого буфер библиотек будет заполнен. Когда буфер уже заполнен, печать будет ждать, пока буфер не опустеет (что не происходит при выключенных прерываниях).
- Переместите весь код, который на самом деле не критичен по времени, в оператор if в функции основного цикла. Оператор if должен быть активирован флагом, который будет установлен в ISR (обязательно объявите его volatile).
- Убедитесь, что в вашем коде нет ничего блокирующего (без
delay()
, без ожидания события в бесконечном цикле). Это также может потребовать серьезного рефакторинга, но это важно, если вы хотите получить быструю реакцию за пределами ISR. Возможно, вам стоит изучить конечные автоматы для этого (их довольно легко реализовать, и это очень мощная конструкция). - Держите отладочные отпечатки небольшими, чтобы они не испортили время.
- Наконец: создайте документацию. Обычно этим пренебрегают, потому что это не очень полезно, но может сэкономить много часов следующему программисту, которому нужно поддерживать ваш код.
Это может показаться очень трудоемким, трудным и не таким, как исчерпывающее руководство, которое вы искали. Именно по этой причине поддержка устаревшего кода в большинстве случаев является настоящей головной болью и стоит компаниям больших денег.
В тот или иной момент технический долг должен быть оплачен. Изобретатель кода явно этого не сделал (иначе он/она не написал бы такой большой ISR), так что теперь вам, скорее всего, придется пойти трудным путем.
Проблема также в том, что он имеет флэш-память 32 КБ, RTOS, вероятно, не будет работать, или есть какая-то крошечная версия ;-)?, @Tom
Связанный сайт показывает это: // Устройство: loop() -> FreeRTOS | Дополнительное хранилище программ
// Uno: 444 -> 7018 | 20%
, а у Uno флэш-память всего 32 КБ. Конечно, это сомнительно, если остальная часть вашего кода помещается сверху, но этого нельзя сказать без фактического кода., @chrisl
Конечно нет - не такой длинный код, но есть 24px GFX и сжатые шрифты, так что почти все заполнено, предположим, что шрифты занимают 1/2 - 3/4 памяти. Несжатые шрифты заполнили бы всю память, сжатие делает их 1/2, поэтому 16 КБ без запрошенного графического интерфейса?, @Tom
2 используемых сжатых шрифта GFX также находятся в моем симуляторе .Net здесь https://github.com/eltomjan/ETEhomeTools/blob/master/ArduinoSimulator/Font_big.h и https://github.com/eltomjan/ETEhomeTools/blob/master /ArduinoSimulator/Font_medium.h, @Tom
Итак, вы застряли с рефакторингом ISR, чтобы уменьшить его., @chrisl
- Как объявить массив переменного размера (глобально)
- Как я могу прервать задержку() при нажатии кнопки?
- Каковы традиционные способы оптимизации использования программной памяти?
- Объявление переменной внутри основного цикла
- hx711 и датчик нагрузки показывают 0 для калибровки
- Как быстро и без задержки переместить сервопривод?
- Светодиод мигает с помощью кнопки
- Оптимизированный генератор случайных буквенно-цифровых строк
Рефакторинг, чтобы не использовать большие ISR. ISR должны быть короткими и быстрыми. «Большой ISR» — фундаментальный недостаток. Вы хотите, чтобы ISR выполняли как можно меньше циклов (вплоть до того, чтобы не использовать функции Arduino более высокого уровня, такие как
digitalRead()
. ISR должен выполнять как можно меньше циклов команд., @Duncan CМне приходилось делать это много раз с «кодом» клиентов. Шаг первый — сохранить ту функциональность, которую вы можете использовать в библиотеках. Второй шаг — написать новую программу с использованием этих библиотек., @Majenko
@Majenko, вы занимаетесь платной разработкой Arduino?, @Duncan C
Да, хотя я предпочитаю этого не делать. Обычно я работаю с более мощными системами, чем Arduino., @Majenko
Установите флаг в прерывании. И сделайте проверку цикла для этого флага, чтобы справиться с ситуацией. И убедитесь, что этот цикл работает как можно быстрее, устранив задержки/задержки. Это можно сделать с помощью конечного автомата., @Paul
Ищу еще 4 конструктивных ответа. Кстати, вещи, связанные с ISR или безопасностью, было бы неплохо иметь в ассемблере, но это не так просто, как в Atmel Studio - не так ли?, @Tom
Где код для проверки?, @Mikael Patel
Никто бы не понял, и это собственность изобретателя, для которого я ремонтировал. Вопрос в том, есть ли какие-либо «легкие» шаблоны или шаблоны дизайна для начала? Вероятно, единственная общедоступная часть - это сжатие шрифтов GFX (в среднем до 1/2), она есть у меня на Github, но, вероятно, не нуждается в улучшении или рефакторинге..., @Tom
Вы ничего не написали о безопасности в вопросе. Что ты имеешь в виду. Также: вы уверены, что код позволяет прерывать ISR? Это затруднило бы рефакторинг программы и могло бы стать причиной дисфункции. Но без публикации кода вы получите только очень расплывчатые ответы, в основном состоящие из «рефакторинга кода для использования более коротких ISR»., @chrisl
Это проект измерения и регулирования, работающий даже с газом, поэтому безопасность очень важна. Я думаю, что где-то внутри ISR есть какое-то прерывание разрешения, и иногда оно делает непредсказуемые вещи - печатает в случайных цветах / положениях и т. Д. И это очень сложно или невозможно воспроизвести вне реальной среды. Разве нет готового «шаблона» или шаблона проектирования для чего-то похожего на ОС реального времени ;-)?, @Tom