Потоковая передача данных для конечного автомата - PullRequest
0 голосов
/ 02 сентября 2010

У меня есть конечный автомат, который должен поддерживать воспроизведение.У нас есть состояния, которые выполняют действия, и иногда нам нужно генерировать случайные числа.Если программа завершает работу во время выполнения FSM, программе необходимо воспроизвести весь FSM, используя те же случайные числа, что и раньше.

Для базового примера, скажем, у меня было три состояния: A, B и C. FSM вызовет функцию Execute () состояния.В конце функции штат опубликует событие, и FSM определит, к какому состоянию перейти.В состоянии A он вызывает rand ().Если число четное, оно отправит событие для перехода в состояние B, в противном случае состояние C должно быть следующим состоянием.

void StateA::Execute(IEventQueue& rQueue)
{
    int num = rand();
    if( num % 2 == 0 )
    {
        rQueue.PostEvent("GoToStateB");
    }
    else
    {
        rQueue.PostEvent("GoToStateC");
    }
}

Если случайное число равно 69, то оно должно перейти в состояние C. В состоянии C программа может завершиться.Когда программа запускается снова, она должна воспроизвести конечный автомат.Очевидно, что для правильной работы он не может сгенерировать совершенно новое случайное число, для точного воспроизведения необходимо снова использовать 69.

У меня есть интерфейс файлового потока, который я могу использовать для сохранения данных вфайл, но код немного уродлив:

void StateA::Execute(IEventQueue& rQueue, IFileStream& rStream)
{

    int num = 0;

    // fails if there's no more data to read
    bool bSuccess = rStream.ReadInt(num);
    if (!bSucess)
    {
        num = rand();
        rStream.WriteInt(num);
    }

    // same code as before
}

Моя единственная проблема с этим решением состоит в том, что мне все равно, нужно ли сначала проверять поток данных и затем условно записывать в тот же поток.

Я думал о том, чтобы скрыть это так:

void StateA::Execute(IEventQueue& rQueue, IStream& rStream)
{

    int num = 0;

    num = rand();
    rStream & num;

    // same code as before
}

Внутри IStream оператор & (вероятно, не лучшее использование перегрузки) фактически попытается прочитать int из потока.Если бы этот поток был пустым, он записал бы его вместо этого.Как и раньше, поведение будет таким: сначала читать до конца потока, а затем начинать добавлять.

Поэтому я предполагаю, что мой вопрос таков: есть ли общая идиома для этого типа воспроизведения, которую я мог бы пропустить?У кого-нибудь есть альтернативные предложения?Я чувствую, что начинаю немного усложнять дизайн.

Спасибо!

Ответы [ 3 ]

1 голос
/ 02 сентября 2010

Почему состояния взаимодействуют напрямую с файловым потоком?Одиночная ответственность говорит, что у нас должен быть класс, задачей которого является предоставление правильного номера на основе некоторой логики.

struct INumberSource {
    virtual int GenNextNumber() = 0;
}

// My job is to provide numbers from an RNG
struct RNGNumberSource : public INumberSource {
    virtual int GenNextNumber() {
        return rand();
    }
}

// My job is to write any numbers sourced through me to a file
// I delegate to another source to get an actual number
class FileStreamTrackingNumberSource : INumberSource {
public:
    FileStreamTrackingNumberSource(INumberSource& source, IFileStream& stream)
        : altSource(source), fileStream(stream) { }

    virtual int GenNextNumber() {
        int num = altSource.GenNextNumber();
        fileStream.WriteInt(num);
        return num;
    }
private:
    INumberSource altSource;
    IFileStream& fileStream;
}

// My job is to source numbers from a file stream delegating to an
// alternate source when I run out
class FileStreamNumberSource : public INumberSource {
public:
    FileStreamNumberSource(INumberSource& source, IFileStream& stream)
        : altSource(source), fileStream(stream), failedRead(false) { }

    virtual int GenNextNumber() {
        int num = 0;

        if(failedRead || !(failedRead = fileStream.ReadInt(num))) {
            num = altSource.GenNextNumber();
        }

        return num;
    }

private:
    INumberSource& altSource;
    IFileStream& fileStream;
    bool failedRead;
}

Так что в вашем случае вы бы предоставили IFileStream и RNGNumberSource для FileStreamTrackingNumberSource и предоставить то же самое IFileStream до FileStreamNumberSource.Это FileStreamNumberSource - это то, что вы дадите параметру INumberSource вашего штата.

Если вам нужно только число, чтобы выбрать следующее состояние, тогда ваш код состояния может выглядеть следующим образом:

void StateA::Execute(IEventQueue& rQueue, INumberSource& numberSource)
{
    if( numberSource.GenNextNumber() % 2 == 0 )
    {
        rQueue.PostEvent("GoToStateB");
    }
    else
    {
        rQueue.PostEvent("GoToStateC");
    }
}
0 голосов
/ 02 сентября 2010

Я не уверен, что понимаю обоснование «воспроизведения», но разве вы не можете просто обернуть всю логику «случайное число или чтение из файла» за класс или функцию?

UPDATE

Что касается «воспроизведения» и вашего дизайна в целом, я не уверен, что для FSM нормально генерировать собственный стимул (то есть случайные числа, которые в свою очередь вызывают переходы состояний). Как правило, стимул предоставляется извне. Если учесть это заново, то у вас больше не будет этой грязной проблемы!

0 голосов
/ 02 сентября 2010

Я подозреваю, что у вас должно быть два файла: один, который записывает события, которые вы играете, и другой, из которого вы читаете события «повторного воспроизведения».Если файл повторного воспроизведения длиннее, чем файл «записи», то именно этот файл вы используете для повторного воспроизведения.

Я бы также не использовал перегрузку операторов, как вы предложили.Возможно, просто используйте троичный оператор.

...