Pull to refresh

Comments 16

Это то, чего вам не хватает.
Это то, без чего я прекрасно обхожусь. Но это не значит, что избегаю. Использую. Но не на уровне вычислений. Впрочем, подробнее в моей предыдущей статье — https://habr.com/ru/post/483610/

Мне кажется, или Вы переизобрели data drven progeamming?

Если имеется в виду потоковое программирование, то нет. Автоматное программирование — это другая модель. Работа автоматов никак не привязана к готовности данных. Хотя можно сделать и это. Т.е. автоматное программирование покрывает поточную модель программирования, но не наоборот.

Чтобы быть уверенными о том, что говорим об одном и том же: вики. Я не буду утверждать, что полностью понимаю ваши статьи, не настолько знаю теорию автоматов, поэтому прошу объяснить разницу "на пальцах". Когда мы пишем в стиле data driven, получаются цепочки вызовов функций и движок, который задаёт "такты" для синхронизации. Точно так же перед функцией может быть буфер памяти, если функция требует несколько входов или для реализации асинхронности.

Похоже мы говорим об одном. Я просто брал за основу аналог на «русском вики»…

Но лучше не «на пальцах», а на каком-нибудь простом примере. В моей статье приведена модель элемента И-НЕ. Даже две — с двумя и с одним состоянием. Возьмем для начала именно ее и именно с одним состоянием. Здесь мы имеем функцию — И-НЕ (y = !(x1&&x2). Функция она и в Африке функция, так? Т.е. и у Вас и у меня — это функции…
Теперь об отличии. В потоковом программировании функция запускается только при готовности данных. Т.е. должны быть «готовы» данные — x1 и x2. Только тогда и будет запущена «функция И-НЕ». Т.е. процесс в этом случае пассивный, т.к. ждет готовности данных.
Автомат — активный процесс. Он не ждет готовности, а берет то, что есть на текущий момент на входах. Т.е. он работает и когда готов только x1, или только x2, или готовы x1 и x2, да даже когда они оба не готовы.
Теперь, если мы возьмем две функции И-НЕ и соединим их между собой по схеме триггера, то в терминах потокового программирования получим цепочку из двух функций, где выход первой «готовится» на один из входов второй, а выход второй «готовится» на вход первой функции. Имеем цепочку из двух функций, обменивающихся данными.
Я правильно понял то, что Вы именно так бы реализовали «потоковый RS-триггер»?

Да, примерно так, хотя в дистиллированной формулировке DDP предполагает ациклический восходящий граф ценой дублирования функций. Когда заполняются оба входа, функция добавляется в очередь на выполнение. Активный автомат получается как асинхронная/самосинхронная схема без тактового генератора? Я вот честно не понимаю, как автомат должен работать, когда второй вход не готов, разве что эмитировать аналог null. А ещё не понимаю, что значит "берёт"? У него есть, условно, ссылка не только на входной буфер потребителя, но и на выходной буфер поставщика?

Вот картинка потоковой программы из книги Котов и др. Элементы параллельного программирования (можно скачать из инета, например, Z-Library)
image
Здесь вычисляются два выражения x = a*c- b*d; и y = a*d+b*c;
Не знаю как Вы это реализуете, а я — по типу RS-триггера. У меня каждый «квадратик» будет автомат (по типу элемента И-НЕ только операции арифметические), ну и связи как у триггера. Все просто и логично.
Как будет работать? Да просто. На входы подаем значения и через три такта на выходах в динамике получаем результаты. Т.е. все работает как обычная логическая (цифровая) схема.
Будут, наверное, гонки до установившегося значения (в течение трех тактов). Но без проблем сделать автоматы, которые бы работали по готовности.
Я вот честно не понимаю, как автомат должен работать, когда второй вход не готов

В данной ситуации он (автомат) и не думает о готовности. Он, поскольку активный, постоянно (с дискретным временем) «лопатит» все что поступает на входы и выдает на выход. Но, еще раз, чтобы было по готовности данных, то просто автомат будет посложнее, т.к. нужно будет синхронизироваться с входными данными.
«Тактовый генератор», конечно, есть. Нужно же как-то реализовать модель дискретного времени. Он он также «зарыт» как тактовый генератор процессора. У него-то он тоже есть, но Вы о нем просто не задумываетесь. Так и я — «рисую» автомат (см. тот же Листинг 3), а что и как тактировать решает уже среда интерпретации автоматов, т.е. ВКПа.
По поводу «берет». См. Листинг 3. Пусть вход x1. Есть ссылка на переменную и есть предикат x1(), который с ней работает. Но вместо ссылки это могла быть и переменная типа bool. Вы, кстати, какой язык используете? Для С++ все, надеюсь, просто следует из приведенного кода.

На плюсах не пишу, но читаю. Можете сказать, что происходит в pVarX1->GetDataSrc() и как обрабатывается LArc? Ну и содержания lfsaappl.h я ни в одной из статей не нашёл. В идеале хотел бы увидеть программу, которая использует простой И-НЕ, принимая на вход два аргумента и выдавая в консоль результат.

Можете сказать, что происходит в pVarX1->GetDataSrc()

Замечу, что сначала создается объект типа CVar — локальная переменная автомата в теневой памяти:
pVarX1 = CreateLocVar(«x1», CLocVar::vtBool, «локальный 1-й вход»);
Она становится доступной не только на уровне кода, но и на уровне визуальной средs исполнения автоматов ВКПа.
GetDataSrc() — метод чтения ее значения;
SetDataSrc(...) — метод установки ее значения.
как обрабатывается LArc?

Объекты типа LArc создают внутреннюю [битовую] структуру, необходимую для быстрой интерпретации автомата с необходимыми ссылками внутри этой структуры. Далее ее адрес передается базовому автоматному объекту типа LFsaAppl.
LFsaAppl.h
#ifndef LFSAAPPL_H
#define LFSAAPPL_H

#include <QObject>
#include <string>
#include <list>
using namespace std;

#include "./FSA/task.h"
#include "./SQM/larc.h"
#include "./FSA/LFsaHdr.h"
//#pragma warning( disable: 4251 )	// давить warning
#include "./VARFSA/csetlocvar.h"

#include "./VARFSA/Var.h"
typedef unsigned int UINT;
extern string itoa(int n);

class LFsaAppl;
class TNetFsa;
class LListState;
class LFsaArc;
class WSP;
typedef int(LFsaAppl::*FncClassX)();
typedef void(LFsaAppl::*FncClassY)();
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                             fs_tbl
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class fs_tbl {
 public:
  LListState* adt; FncClassX* adx; FncClassY* ady;
public:
  fs_tbl(LListState *pAdt, FncClassX *pAdx, FncClassY *pAdy);
  ~fs_tbl();
};
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                             fs_dbg
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class fs_dbg {
 public:
    string nameFsa;
    string nameFsaProcess;
    int trace;
    fs_dbg(string szName, int nTrace=0);
    ~fs_dbg();
};
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                             LFsaAppl
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class CSetVarLDV35;
class CFDelay;
class CVarPrmProc;
class CVarFsaLibrary;
class CVarFSA;
class TAppCore;
class LFsaAppl:public fs_tbl, public fs_dbg  {
public:
    virtual string	CurrentState(int nNum=-1);
    // локальные переменные
    // реакция на левую кнопку мышки
        virtual void VirtLButtonDown();
        CVar *pVarIfLButtonDown;
        bool bIfLbuttonDown;
    //
    // реакция на правую кнопку мышки
        virtual void VirtRButtonDown();
        CVar *pVarIfRButtonDown;
        bool bIfRbuttonDown;
    //
    string			WhatState(int n=-1);
    int				FGetRange();
    LFsaAppl*		FGetPtrFsaAppl(string strNameVarFSA);
    bool			FIsActiveParDelay();
    void			FCreateDelay(int nDelay);
    void			FCreateParDelay(int nDelay);
    bool			FSavVariable(CVar* pV);
    virtual bool	FCopyFSA(string strPatchSrc, string strPatchDst, bool bIfInfoDlg = true);
    virtual bool	FDeleteFSA(string strPatchSrc, string strPatchDst, bool bIfInfoDlg = true);
    bool			FDbgGetViewArc();
    bool			FDbgGet();
    bool			FDbgSet(bool b);
    bool			FDbgSetViewArc(bool b);
    virtual bool	FSetLocking(bool bSet, LFsaAppl *pOwner, string strNameVar);			//	установка/сброс блокировки определенным процессом
    virtual void	FDbgViewCurrentArc();
    virtual string	FDbgViewArc(UINT id = 29003);
    virtual string	FDbgViewState(UINT id = 29003);
    void			FUpdateVariable();                                      // обновить локальные переменные
    void			FDrawVariable();                                        // отобразить/нарисовать локальные переменные
    virtual bool	FCreationOfLinksForVariables();					// создание ссылок для переменных процесса
    virtual bool	FUpdateLinks(string name, int nT);				// отредактировать связи процесса (имя процесса, тип редакции)
    virtual bool	FInit();
    virtual LFsaAppl* Create(CVarFSA *pCVF);
    LFsaAppl();
    LFsaAppl(LArc* aT, string name, CVarFSA *pCVF, CVarFsaLibrary *pCVFL, int trace=0);
    LFsaAppl(LArc* aT, string name);
    virtual ~LFsaAppl();
    int virtual x1();   int virtual x2();   int virtual x3();
    int virtual x4();   int virtual x5();   int virtual x6();
    int virtual x7();   int virtual x8();   int virtual x9();
    int virtual x10();  int virtual x11();  int virtual x12();
    int virtual x13();  int virtual x14();  int virtual x15();
    int virtual x16();
    int virtual x17();  int virtual x18();  int virtual x19();
    int virtual x20();  int virtual x21();  int virtual x22();
    int virtual x23();  int virtual x24();  int virtual x25();
    int virtual x26();  int virtual x27();  int virtual x28();
    int virtual x29();  int virtual x30();  int virtual x31();
    int virtual x32();
    void virtual y1(){}   void virtual y2(){}   void virtual y3(){}
    void virtual y4(){}   void virtual y5(){}   void virtual y6(){}
    void virtual y7(){}   void virtual y8(){}   void virtual y9(){}
    void virtual y10(){}  void virtual y11(){}  void virtual y12(){}
    void virtual y13(){}  void virtual y14(){}  void virtual y15(){}
    void virtual y16(){}
    void virtual y17(){}  void virtual y18(){}  void virtual y19(){}
    void virtual y20(){}  void virtual y21(){}  void virtual y22(){}
    void virtual y23(){}  void virtual y24(){}  void virtual y25(){}
    void virtual y26(){}  void virtual y27(){}  void virtual y28(){}
    void virtual y29(){}  void virtual y30(){}  void virtual y31(){}
    void virtual y32(){}
    void virtual MooreAction(){}
    void virtual ELSE();
    void virtual MealyLoop();

    virtual double* GetPtrDblArray(int nNum, int *nSize);
    virtual double* SetPtrDblArray(int nNum, double *pArray, int *nSize);
    virtual int* GetPtrIntArray(int nNum, int *nSize);
    virtual int* SetPtrIntArray(int nNum, int *pArray, int *nSize);
    virtual char* GetPtrCharArray(int nNum, int *nSize);
    virtual char* SetPtrCharArray(int nNum, char *pArray, int *nSize);
    virtual QByteArray* GetPtrByteArray(int nNum);

    CVarPrmProc* GreateParameters();
    CVarPrmProc* GetPtrParameters();
    virtual void CleaningWindow();
    virtual CVar* FSetPtrVarParameter(LFsaAppl *pLFsa, CVar *pVar, int  nNum);
    virtual CVar* FGetPtrVarParameter(LFsaAppl *pLFsa, int  nNum);
    virtual double FSetParameter(LFsaAppl *pLFsa, double d, int  nNum);
    virtual string FSetParameter(LFsaAppl *pLFsa, string str, int  nNum);
    virtual void FUpdateParameter(LFsaAppl *pLFsa, int  nNum);
    virtual double FUpdateDataParameter(LFsaAppl *pLFsa, int  nNum);
    virtual string FUpdateStringParameter(LFsaAppl *pLFsa, int  nNum);
    virtual double FGetDataParameter(LFsaAppl *pLFsa, int  nNum);
    virtual string FGetStringParameter(LFsaAppl *pLFsa, int  nNum);
    virtual void ExecuteThreadStep();
    virtual void WaitForThreadToFinish();
    bool	IfCall();
    void	FSetSleep(long lS);
    long	FGetSleep();
    void	FStart();
    void	FStop();
    bool	FIfStop();
    void	FContinue();
    TASK*	FLoad(TNetFsa *NetFsa, string strProc, int pri, bool bNot, TAppCore *pCore, CVarFSA *pVar);
    void	FCancelTsk();
    TASK*	FGetTask();
    virtual string	FGetState(int nNum=-1);
    string	FGetNextState();
    string	FGetStateUp();
    void	FSetNameFsa(const string nam);
    void	FSetNameProcess(const string nam);
    string	FGetNameFsa() const;
    string	FGetNameProcess() const;
    string	FGetNameVarFSA() const;
    LFsaAppl*   FCall(LFsaAppl*);
    int		FIsActiveTask();
    bool 	FIfOk();
    void 	FSetOk(int nOk=1, const char* pch=nullptr);
    void 	FSetError(int nOk=1, const char* pch=nullptr);
    const char*	FGetError(int* pn=nullptr);
    const char* FGetOk(int* pn=nullptr);
    void 	FClearOk();
    void 	FClearError();
    void	FPrefixErrOk(string &str);
    void	FPrefixErrOk(char str[]);
    CLocVar*    GetAddressVar(string nam);
    CLocVar*    CreateLocVar(string namVar, unsigned int unType, string strComment, bool bSave=true);
    long	GetNumberOfTasks(long *l);
    TNetFsa*	GetPointerToNet();
    string	strGetCurrentArc(WSP *p=nullptr);
    string	strCurrentArc;
 private:
    inline int X1();	inline int X2();	inline int X3();  inline int X4();
    inline int X5();	inline int X6();	inline int X7();  inline int X8();
    inline int X9();	inline int X10();	inline int X11(); inline int X12();
    inline int X13();inline int X14(); inline int X15(); inline int X16();
    inline int X17();inline int X18(); inline int X19(); inline int X20();
    inline int X21();inline int X22(); inline int X23(); inline int X24();
    inline int X25();inline int X26(); inline int X27(); inline int X28();
    inline int X29();inline int X30(); inline int X31(); inline int X32();

public:
    TASK*		CALTSK(TNetFsa *NetFsa, int pri);
    LFsaHdr		var;
    LFsaHdr		*pLFsaHdr;
    LFsaAppl	*pFsaApplError;
    int			nFsaPriority;
    string		strNameVarFsaNew;
    TAppCore	*pTAppCore;
    CVarFSA		*pCVarFSA;
    CVarFsaLibrary	*pCVarFsaLibrary;
    CVarPrmProc *pVarPrmProc;
    CSetLocVar	*pCSetLocVar;
    CSetVarLDV35    *pCSetVarLDV35;
    int	nModeVar;
    LArc		*pTBL;
    LFsaAppl	*pNestedAutomaton;
    LFsaAppl	*pMainAutomaton;

protected:
    LFsaArc		*ADT, *ARC_T, *ARC_B;
    unsigned long M_X, M_XY, BAZ_XY;
    LListState* SqmTable;
    FncClassX	X[32];
    FncClassY	Y[32];
    WSP*		pWSP;
    bool		bIfViewArc;
    bool		bIfDebug;
    CFDelay		*pParFDelay;
    CFDelay		*pCFDelay;
    bool                bIfRunThread{false};
private:
    unsigned long p2pck;
    string		strOk;
    string		strError;
    int			nOkProcess;
    int			nErrorProcess;
    TNetFsa		*pTNetFsa;
    bool		bIfCall;
    friend class WSP;
    friend class TNetFsa;
};
typedef list<LFsaAppl> CArrayLFsaAppl;
typedef list<LFsaAppl>::iterator CIteratorLFsaAppl;
typedef list<LFsaAppl*> CArrayILFsaAppl;
typedef list<LFsaAppl*>::iterator CIteratorILFsaAppl;

#endif // LFSAAPPL_H


Код простого И-НЕ. Без теневой памяти и локальных переменных. Обращение к нему — через свойства класса, где bX1, bX2 — входы элемента, bY — выход.
Простой И-НЕ

#include "lfsaappl.h"

class FIneSimple :
    public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FIneSimple(pTAppCore, nameFsa, pCVarFsaLibrary); }
    FIneSimple(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL);
    bool bX1, bX2, bY;
protected:
    int x1(); int x2();
    void y1(); void y2();
};

#include "stdafx.h"
#include "FIneSimple.h"
// state machine transition table
static LArc TBL_IneSimple[] = {
// установка в правильное начальное состояние
    LArc("st",		"s1","^x1",	"y1"),			//
    LArc("st",		"s1","^x2",	"y1"),			//
    LArc("st",		"s0","x1x2","y2"),			//
// модель И-НЕ
    LArc("s1",		"s0","x1x2","y2"),			//
    LArc("s0",		"s1","^x1",	"y1"),			//
    LArc("s0",		"s1","^x2",	"y1"),			//
    LArc()
};

FIneSimple::FIneSimple(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL):
    LFsaAppl(TBL_IneSimple, strNam, nullptr, pCVFL)
{
}

int FIneSimple::x1() { return bX1; }

int FIneSimple::x2() { return bX2; }

void FIneSimple::y1() { bY = true; }
//
void FIneSimple::y2() { bY = false; }



Можете подсказать, где лежит LFsaAppl.h и кто автор? Как-то не совсем понятно, как скомпилировать и использовать код из статей. Я погуглил, понял, что это не ваша самописка, похоже, это часть fsamdi, которая кочует из рук в руки, но на гитхабе не появляется. Кто, как и я, хотел бы объяснения работы на пальцах АП в качестве старта, чтобы понимать, что lws0954 привнёс нового, вот статья 2000го из журнала "Мир ПК" про задачу Майхилла.

Можете подсказать, где лежит LFsaAppl.h и кто автор?

Разрешите представиться. Я и есть. Среда ВКПа это современное развитие того, что раньше называлось КА-технология (См. раздел КА-технология на сайте SoftCraft www.softcraft.ru/auto).

По поводу выложить (естественный вопрос). Во-первых, как-то не дошло дело до гитхаба и, если честно, просто не знаю, не пробовал это делать. А, во-вторых, после ухода с Visual Studio и MFC на Qt Creator и, собственно, Qt нужно просто довести до полностью рабочего варианта. Я, конечно, не испытываю проблем, но нужно, чтобы это же «испытывали» и другие.
чтобы понимать, что lws0954 привнёс нового
Идея в основном осталась той же, но «по мелочевке» набежало достаточно много. Например, я не помню уже была ли там теневая память. А ее наличие очень и очень важно. Это по-крупному. А сама концепция автоматной модели (в лице базового класса LFsaAppl) по большому счету не изменилась. Но можно, конечно, просто сравнить заголовки lfsaappl.h, чтобы понять что добавилось нового по сравнению с 2000-м годом. Больше развитие связано со средой. Сейчас в ней можно визуально проектировать автоматы без всякого С++. Правда, не так эффективно, как на языке, но зато быстро и прямо на лету.
PS
В попытках вспомнить даже ностальгия «прошибла» :)

Mea culpa, не сравнил авторство статей :) Но если честно, та, которая из "Мира ПК", как-то нагляднее, там проще и последовательнее, а в этой серии статей академичнее, но надо прочесть хоть одну из книг по КА из списка в конце. Спасибо за ссылку на код, попробую собрать и запустить, вот чувствую, что не понимаю чего-то простого именно в реализации. Концепцию теневой памяти понимаю, а что значит "забирает входные данные" — нет. Если не разберусь при отладке, не подскажете именно по реализации?

а в этой серии статей академичнее
Собственно такая цель и поставлена. Популярно я много написал и, видимо, пришла пора «академичности» ;)
Если не разберусь при отладке, не подскажете именно по реализации?
Конечно. Сделаю это с удовольствием ;)
Sign up to leave a comment.

Articles