# Простой счётчик кода Грея

Часто нам нужны компоненты, которые не являются исключительно комбинационными по своей сути - то есть, мы хотим, чтобы компонент имел некоторую память. Существует важная тонкость в объявлении таких компонентов: у вас не может быть компонента, который сам по себе хранит состояние, так как конкретный компонент может встречаться несколько раз в той же схеме. Он не может появиться в схеме несколько раз непосредственно, но он может появиться несколько раз, если он встречается в подсхеме, которая используется несколько раз.

Решение состоит в том, чтобы создать новый класс для представления текущего состояния объекта, и ассоциировать его экземпляры с компонентом через состояние родительской схемы. В этом примере, который реализует реагирующий на фронт 4-битный счётчик кода Грея, мы объявляем класс `CounterData` для представления состояния счётчика, в дополнение к подклассу `InstanceFactory`, как было показано раньше. Объект `CounterData` запоминает как состояние текущего состояния счётчика, так и последнее состояние тактового входа (чтобы обнаруживать передние фронты).

## CounterData

    package com.cburch.gray;

    import com.cburch.logisim.data.BitWidth;
    import com.cburch.logisim.data.Value;
    import com.cburch.logisim.instance.InstanceData;
    import com.cburch.logisim.instance.InstanceState;

    /** Представляет состояние счетчика. */
    class CounterData implements InstanceData, Cloneable {
        /** Получает состояние, связанное с этим счётчиком в состоянии схемы,
         * генерируя состояние, если необходимо.
         */
        public static CounterData get(InstanceState state, BitWidth width) {
            CounterData ret = (CounterData) state.getData();
            if(ret == null) {
                // Если оно ещё не существует, то мы установим его на наши
                // значения по умолчанию, и добавим его в состояние схемы, так что оно может быть получено
                // в следующих просчётах.
                ret = new CounterData(null, Value.createKnown(width, 0));
                state.setData(ret);
            } else if(!ret.value.getBitWidth().equals(width)) {
                ret.value = ret.value.extendWidth(width.getWidth(), Value.FALSE);
            }
            return ret;
        }

        /** Последнее наблюдаемое значение на тактовом входе. */
        private Value lastClock;

        /** Текущее значение, выдаваемое счётчиком. */
        private Value value;

        /** Создает состояние с заданными значениями. */
        public CounterData(Value lastClock, Value value) {
            this.lastClock = lastClock;
            this.value = value;
        }

        /** Возвращает копию этого объекта. */
        public Object clone() {
            // Мы можем просто использовать то, что возвращает super.clone(): только переменные экземпляра являются
            // объектами Value, которые неизменяемы, так что нас не волнует, что и копия,
            // и оригинал ссылаются на одни и те же объекты Value. Если бы мы имели изменяемые переменные экземпляра,
            // то, конечно, нам пришлось бы клонировать их.
            try { return super.clone(); }
            catch(CloneNotSupportedException e) { return null; }
        }

        /** Обновляет последнее наблюдаемое значение на тактовом входе, возвращая истину, если он сработал. */
        public boolean updateClock(Value value) {
            Value old = lastClock;
            lastClock = value;
            return old == Value.FALSE && value == Value.TRUE;
        }

        /** Возвращает текущее значение, выдаваемое счётчиком. */
        public Value getValue() {
            return value;
        }

        /** Обновляет текущее значение, выдаваемое счётчиком. */
        public void setValue(Value value) {
            this.value = value;
        }
    }

## SimpleCounter

    package com.cburch.gray;

    import com.cburch.logisim.data.BitWidth;
    import com.cburch.logisim.data.Bounds;
    import com.cburch.logisim.data.Direction;
    import com.cburch.logisim.instance.InstanceFactory;
    import com.cburch.logisim.instance.InstancePainter;
    import com.cburch.logisim.instance.InstanceState;
    import com.cburch.logisim.instance.Port;
    import com.cburch.logisim.util.GraphicsUtil;
    import com.cburch.logisim.util.StringUtil;

    /** Создаёт простой счётчик, который пробегает по очереди 4-битные коды Грея. Этот
     * пример показывает, как компонент может сохранять своё внутреннее состояние. Весь
     * код, относящийся к состоянию, появляется в классе CounterData. */
    class SimpleGrayCounter extends InstanceFactory {
        private static final BitWidth BIT_WIDTH = BitWidth.create(4);

        // Опять же, заметьте, что у нас нет никаких переменных экземпляра, связанных с
        // состоянием конкретного экземпляра. Мы не можем поместить их здесь, потому что только один
        // объект SimpleGrayCounter вообще создаётся, и его работа - управлять всеми
        // экземплярами, появляющимися во всех схемах.

        public SimpleGrayCounter() {
            super("Gray Counter (Simple)");
            setOffsetBounds(Bounds.create(-30, -15, 30, 30));
            setPorts(new Port[] {
                    new Port(-30, 0, Port.INPUT, 1),
                    new Port(  0, 0, Port.OUTPUT, BIT_WIDTH.getWidth()),
            });
        }

        public void propagate(InstanceState state) {
            // Здесь я получаю состояние, связанное с этим компонентом через вспомогательный
            // метод. В данном случае состояние - это объект CounterData, который также есть
            // там, где объявлен вспомогательный метод. Этот вспомогательный метод закончит
            // созданием объекта CounterData, если ещё ни одного не существует.
            CounterData cur = CounterData.get(state, BIT_WIDTH);

            boolean trigger = cur.updateClock(state.getPort(0));
            if(trigger) cur.setValue(GrayIncrementer.nextGray(cur.getValue()));
            state.setPort(1, cur.getValue(), 9);

            // (Вы, возможно, подумываете о том, чтобы определить текущее состояние счётчика
            // через state.getPort(1). Это ошибка, потому что другой
            // компонент может записывать значение в эту же точку, и это
            // "испортит" значение, содержащееся там. Нам действительно нужно хранить
            // текущее значение в экземпляре.)
        }

        public void paintInstance(InstancePainter painter) {
            painter.drawBounds();
            painter.drawClock(0, Direction.EAST); // draw a triangle on port 0
            painter.drawPort(1); // draw port 1 as just a dot

            // Отобразить текущее значение счётчика по центру прямоугольника.
            // Впрочем, если по контексту показывать состояние не надо (при составлении
            // вида для печати), то пропустить это.
            if(painter.getShowState()) {
                CounterData state = CounterData.get(painter, BIT_WIDTH);
                Bounds bds = painter.getBounds();
                GraphicsUtil.drawCenteredText(painter.getGraphics(),
                        StringUtil.toHexString(BIT_WIDTH.getWidth(), state.getValue().toIntValue()),
                        bds.getX() + bds.getWidth() / 2,
                        bds.getY() + bds.getHeight() / 2);
            }
        }
    }

Next: Gray Code Counter.