15. januar 2002 - 13:32
#9
Jeg starter med at oprette en tråd der kører min simulator. Den opretter et netværk af komponenter som er realiseret igennem en familie af klasser. Moderklassen indeholder statiske 2 bitmaps. Det første bitmap (baggrunden) tegner jeg de statiske elementer op i og gemmer. Det andet bitmap (forgrunden) kopierer jeg baggrunden over i og optegner de dynamiske dele. Når dette er gjort blitter jeg billedet ud i mit clientarea.
Alle mine komponenter er nedarvet fra moderklassen og har derfor adgang til at tegne i de to bitmaps.
Modelleringstråden har adgang til clientareaet igennem et HWND, som jeg gemmer i en struct som er delt imellem hovedprogrammet og modelleringstråden.
Moderklassen:
class WsObject
{
protected:
char* szName; //Name of object
bool invisible; //Flag to indicate if component should be drawn
static unsigned long deltaT; //Milliseconds between each sample
static PCOMMONDATA pdata;
static HBITMAP hForeground;
static HDC hdcF;
static HBITMAP hBackground; //Bitmaps representing foreground and background
static HDC hdcB; // Device contexts for foreground and background
WsObject(); //constructor
public:
static BYTE lsstates[LS_SIZE];
int TYPE;
static bool recalculate;
static vector <WsObject*> allWsObjects; //List of all objects
static long getDeltaT();
static void setDeltaT(long ms);
static HDC getHDC();
static void init(HDC h, PCOMMONDATA p);
static void backToFront();
static PCOMMONDATA pdat;
static void reply(int action, int id, int d0, int d1);
void connect(WsObject* other);
char* getName();
void setInvisible(bool v);
vector <WsObject*> connections;
//Command model to do something
static void command(int ACT, int ID, int D0, int D1);
static void command(MsgFrame m);
virtual void modelCommand(MsgFrame command)=0;
//Drawrelated
virtual Vector getDrawPosition(WsObject* from)=0;
//Flowcalc related
virtual void sendFlow(WsObject* from, double f, WsObject* initiator)=0;
virtual double getControlled(WsObject* from, WsObject* initiator)=0;
virtual double getResOut(WsObject* from)=0;
virtual double getCurrent(WsObject* from)=0;
~WsObject();
//Move microliters
virtual void pushUL(WsObject* from, int type, int recursioncount)=0;
};
Eksempel på komponent:
class WsTube : public WsObject //Tube class
{
private:
void cloneContent();
protected:
ringbuf* contents;
int* olddraw;
double flow; //Current flow in pipe form connection 0 to 1
int iVolume; //Volume of tube in uL
double lResistance; //Flowresistance of tube
public:
WsTube();
WsTube(char* n, int vol, long res);
~WsTube();
Vector getDrawPosition(WsObject* from);
void sendFlow(WsObject* from, double f, WsObject* initiator);
double getControlled(WsObject* from, WsObject* initiator);
double getResOut(WsObject* from);
double getCurrent(WsObject* from);
virtual void pushUL(WsObject* from, int type, int recursioncount);
virtual void modelCommand(MsgFrame command);
int insert(int end, int val);
};
Når jeg starter modelleringstråden op starter jeg med at initalisere de to bitmaps i hukommelsen:
// Initializastion of NetObj.
void WsObject::init(HDC h, PCOMMONDATA p)
{
hdcB = CreateCompatibleDC(h);
hdcF = CreateCompatibleDC(h);
pdat = p;
hBackground = CreateCompatibleBitmap(hdcB, 800, 600);
hForeground = CreateCompatibleBitmap(hdcF, 800, 600);
//Klargør bitmaps
WsObject::hBackground = (HBITMAP)LoadImage(0, \"./backgnd.bmp\", IMAGE_BITMAP, 800,600, LR_VGACOLOR | LR_LOADFROMFILE);
WsObject::hForeground = (HBITMAP)LoadImage(0, \"./backgnd.bmp\", IMAGE_BITMAP, 800,600, LR_VGACOLOR | LR_LOADFROMFILE);
//Its easyer to load twice and garanty compatibility :)
SelectObject(WsObject::hdcB, WsObject::hBackground);
SelectObject(WsObject::hdcF, WsObject::hForeground);
deltaT = 1; //Milliseconds
bool recalculate = true;
}
Optegningen foregår igennem beskeder jeg sender til komponenterne igennem modelCommand():
void WsTube::modelCommand(MsgFrame command)
{
switch(command.ACT)
{
case DRAW_STATIC:
{
if (invisible)break;
Vector a = connections[0]->getDrawPosition(this);
Vector b = connections[1]->getDrawPosition(this);
DeleteObject(SelectObject(WsObject::hdcB, CreatePen(PS_SOLID, 9, 0)));
BeginPath(WsObject::hdcB);
MoveToEx(WsObject::hdcB, a.x, a.y, NULL);
LineTo(WsObject::hdcB, b.x, b.y);
EndPath(WsObject::hdcB);
StrokePath(WsObject::hdcB);
}
break;
case DRAW_DYNAMIC_BACK:
{
if (invisible)break;
Vector a = connections[0]->getDrawPosition(this);
Vector b = connections[1]->getDrawPosition(this);
Vector c = (b-a)/(iVolume+1);
Vector angle = c.unit().cross();
Vector pos;
int tempindex = contents->getIndex();
for(int x = 0; x<iVolume; x++)
{
tempindex++;
if (tempindex > iVolume)
tempindex = 0;
if (olddraw[x]!=contents->getVal(tempindex))
{
DeleteObject(SelectObject(WsObject::hdcB, CreatePen(PS_SOLID, 1, reagents[contents->getVal(tempindex)].Color)));
pos = a+c*(x+1)-angle*2;
BeginPath(WsObject::hdcB);
MoveToEx(WsObject::hdcB, pos.x, pos.y, NULL);
pos = a+c*(x+1)+angle*3;
LineTo(WsObject::hdcB, pos.x, pos.y);
EndPath(WsObject::hdcB);
StrokePath(WsObject::hdcB);
olddraw[x]=contents->getVal(tempindex);
}
}
}
break;
case VERIFY_NET:
if (connections.size()!=2)
TRACE(\"%s connected wrong %d connections (OK=2)\\n\",szName, connections.size());
break;
case RESET_FLOW:
flow = 0;
break;
case DRAW_DATA:
{
if (invisible)break;
Vector a, b;
a = connections[1]->getDrawPosition(this);
b = connections[0]->getDrawPosition(this);
if((a-b).len()>18)
{
Vector pos = a + ((b - a)/2);
char tmp[100];
int size = sprintf(tmp,\"%.1f\", flow);
DeleteObject(SelectObject(WsObject::hdcB, GetStockObject(BLACK_BRUSH)));
TextOut(WsObject::hdcF, pos.x , pos.y, tmp, size);
}
}
break;
default:
break;
};
}
Optegningen foregår på samme måde i alle komponenterne.
Den tråd som opretter klasserne og kører selve simulaerineg ser således ud:
void modThread(PVOID pvoid)
{
unsigned long progress; //Milliseconds passed
PCOMMONDATA pdata = (PCOMMONDATA)pvoid;
pdata->threadModelActive = true;
TRACE(\"Modelthread startup.\\n\");
//Communication variables
MsgFrame buffer;
WsObject::init(GetDC(pdata->hWnd), pdata);
//Simplified ABL735!!!
WsUserInlet Inlet(\"Inlet\", Vector(100,150),-90);
WsTube InletTube(\"InletTube\", 150, 150);
WsJunction InletPhBgJunc(\"InletPhBg\", Vector(100, 300));
WsTube PHBGTube(\"InletTube\", 150, 150);
WsUserPhBg PhBg(\"PhBg\", Vector(250,300),0);
WsTube PHBGELMETTube(\"PHBGELMETTube\", 90, 90);
WsUserElMet ElMet(\"EM\", Vector(400,300),0);
WsTube ElMetOxi(\"InletTube\", 141, 141);
WsUserOxi Oximodule(\"Oximodule\", Vector(601,300), 0);
WsTube OxiPump(\"OxiPump\", 21, 21);
WsUserPump ReagentPump(\"Reagentpump\",PU_REAGENT,Vector(700,300),90 );
WsUserManifold Manifold(\"Manifold\", Vector(700,350), 180);
Inlet.connect(&InletTube);
InletTube.connect(&InletPhBgJunc);
InletPhBgJunc.connect(&PHBGTube);
PHBGTube.connect(&PhBg);
PhBg.connect(&PHBGELMETTube);
PHBGELMETTube.connect(&ElMet);
ElMet.connect(&ElMetOxi);
ElMetOxi.connect(&Oximodule);
Oximodule.connect(&OxiPump);
OxiPump.connect(&ReagentPump);
ReagentPump.connect(&Manifold);
//Verify connections!!!
WsObject::command(VERIFY_NET,0,0,0);
TRACE(\"Network consist of %d individual objects.\\n\", WsObject::allWsObjects.size());
WsObject::command(RESET_FLOW,0,0,0);
WsObject::command(DRAW_STATIC,0,0,0);
WsObject::command(DRAW_DYNAMIC_BACK,0,0,0);
//Prepare image in WsObject
WsObject::backToFront();
WsObject::command(DRAW_DYNAMIC,0,0,0);
//BitBlt(hdc, 0, 0, 800, 600, WsObject::getHDC(), 0, 0, SRCCOPY);
PAINTSTRUCT ps;
BeginPaint(pdata->hWnd, &ps);
progress = timeGetTime();
while(pdata->Keepgoing)
{
//Adcust simulation to realtime
WsObject::setDeltaT((timeGetTime() - progress));
progress = timeGetTime(); //save time
pdata->copyOfDeltaT = WsObject::getDeltaT();
//We dont want to calculate flow every time (Its hard work!!!)
if (WsObject::recalculate)
{
WsObject::command(RESET_FLOW,0,0,0);
WsObject::command(CALCULATE_FLOW,0,0,0);
WsObject::recalculate = false;
}
//Move reagents
WsObject::command(PUSH_UL,0,0,0);
//Check LsSensors
WsObject::command(CHECK_COMPONENT_STATE,0,0,0);
//Draw fluids
WsObject::command(DRAW_DYNAMIC_BACK,0,0,0);
//BitBlt to foreground
WsObject::backToFront();
//Draw dynamic elements, valves pumps, etc.
WsObject::command(DRAW_DYNAMIC,0,0,0);
//Perhaps draw flowinformation (For debug)
//Object::command(DRAW_DATA,0,0,0);
if (pdata->updateClientArea ==true)
{
HDC hdc = GetDC(pdata->hWnd);
//Fetch model image
if (hdc != NULL)
BitBlt(hdc, 0, 0, 800, 600, WsObject::getHDC(), 0, 0, SRCCOPY);
else
TRACE(\"HDC-ERROR\");
ReleaseDC(pdata->hWnd, hdc);
}
//Check for messages
//Must accept at many messages as possible before calculation of flow
//The calculation is very heavy on CPU
while(pdata->inbox.size() > 0)
{
memcpy((char*)&buffer, &pdata->inbox.front(), sizeof(MsgFrame));
pdata->inbox.pop_front(); //Take message out of queue
WsObject::command(buffer);
}
Sleep(100);//Give OS a break
}
pdata->threadModelActive = false;
//EndPaint(pdata->hWnd , &ps);
TRACE(\"model thread ended.\\n\");
}
Det er en stor mundfuld, men jeg sætter pris på enhver hjælp. Det er den sidste bug jeg har i mit eksamensprojekt på diplomingeniøruddannelsen.
/TAX