Started to add code to implement look-ahead.

This commit is contained in:
Adrian Bowyer 2013-05-29 00:10:37 +01:00
parent 438737f2f2
commit 0c77a2f5eb
6 changed files with 280 additions and 31 deletions

View file

@ -48,7 +48,6 @@ Licence: GPL
#define CLIENT_CLOSE_DELAY 1000 // Microseconds to wait after serving a page
#define INDEX_PAGE "reprap.htm"
#define PRINT_PAGE "print.php"
#define MESSAGE_FILE "messages.txt"
#define FOUR04_FILE "html404.htm"
#define KO_START "rr_"
@ -62,4 +61,7 @@ Licence: GPL
#define RING_LENGTH 10
#define LOOK_AHEAD_LENGTH 25
#define LOOK_AHEAD_FLUSH 5
#define LOOK_AHEAD_FUTURE 15
#endif

View file

@ -43,6 +43,8 @@ class GCodeBuffer
int readPointer;
};
//****************************************************************************************************
// The GCode interpreter
class GCodes
@ -61,7 +63,7 @@ class GCodes
boolean ActOnGcode(GCodeBuffer* gb);
boolean SetUpMove(GCodeBuffer* gb);
boolean doDwell(GCodeBuffer *gb);
boolean DoDwell(GCodeBuffer *gb);
Platform* platform;
boolean active;
Webserver* webserver;
@ -69,8 +71,8 @@ class GCodes
boolean dwellWaiting;
GCodeBuffer* webGCode;
GCodeBuffer* fileGCode;
boolean webGCodePending;
boolean fileGCodePending;
boolean webGCodeFinished;
boolean fileGCodeFinished;
boolean moveAvailable;
boolean heatAvailable;
float moveBuffer[DRIVES+1]; // Last is feedrate
@ -83,4 +85,18 @@ class GCodes
int fileToPrint;
};
//*****************************************************************************************************
// Get an Int after a G Code letter
inline int GCodeBuffer::GetIValue()
{
return (int)GetLValue();
}
inline char* GCodeBuffer::Buffer()
{
return gcodeBuffer;
}
#endif

View file

@ -39,8 +39,8 @@ void GCodes::Init()
{
webGCode->Init();
fileGCode->Init();
webGCodePending = false;
fileGCodePending = false;
webGCodeFinished = true;
fileGCodeFinished = true;
active = true;
moveAvailable = false;
heatAvailable = false;
@ -61,22 +61,22 @@ void GCodes::Spin()
if(!active)
return;
if(webGCodePending)
if(!webGCodeFinished)
{
webGCodePending = !ActOnGcode(webGCode);
webGCodeFinished = ActOnGcode(webGCode);
return;
}
if(fileGCodePending)
if(!fileGCodeFinished)
{
fileGCodePending = !ActOnGcode(fileGCode);
fileGCodeFinished = ActOnGcode(fileGCode);
return;
}
if(webserver->GCodeAvailable())
{
if(webGCode->Put(webserver->ReadGCode()))
webGCodePending = !ActOnGcode(webGCode);
webGCodeFinished = ActOnGcode(webGCode);
return;
}
@ -86,9 +86,11 @@ void GCodes::Spin()
if(platform->Read(fileBeingPrinted, b))
{
if(fileGCode->Put(b))
fileGCodePending = !ActOnGcode(fileGCode);
fileGCodeFinished = ActOnGcode(fileGCode);
} else
{
if(fileGCode->Put('\n')) // In case there wasn't one in the file
fileGCodeFinished = ActOnGcode(fileGCode);
platform->Close(fileBeingPrinted);
fileBeingPrinted = -1;
}
@ -97,6 +99,8 @@ void GCodes::Spin()
// Move expects all axis movements to be absolute, and all
// extruder drive moves to be relative. This function serves that.
// If the Move class can't receive the move (i.e. things have to wait)
// this returns false, otherwise true.
boolean GCodes::SetUpMove(GCodeBuffer *gb)
{
@ -154,7 +158,7 @@ void GCodes::QueueFileToPrint(char* fileName)
// Function to handle dwell delays. Return true for
// Dwell finished, false otherwise.
boolean GCodes::doDwell(GCodeBuffer *gb)
boolean GCodes::DoDwell(GCodeBuffer *gb)
{
unsigned long dwell;
@ -187,6 +191,9 @@ boolean GCodes::doDwell(GCodeBuffer *gb)
return false;
}
// If the GCode to act on is completed, this returns true,
// otherwise false.
boolean GCodes::ActOnGcode(GCodeBuffer *gb)
{
int code;
@ -204,7 +211,7 @@ boolean GCodes::ActOnGcode(GCodeBuffer *gb)
break;
case 4: // Dwell
result = doDwell(gb);
result = DoDwell(gb);
break;
case 10: // Set offsets
@ -333,6 +340,8 @@ boolean GCodes::ActOnGcode(GCodeBuffer *gb)
}
}
// An empty buffer jumps straight here and gets disgarded
return result;
}
@ -434,16 +443,6 @@ long GCodeBuffer::GetLValue()
return result;
}
// Get an Int after a G Code letter
int GCodeBuffer::GetIValue()
{
return (int)GetLValue();
}
char* GCodeBuffer::Buffer()
{
return gcodeBuffer;
}

75
Move.h
View file

@ -62,6 +62,30 @@ class DDA
volatile boolean active;
};
class LookAhead
{
friend class Move;
public:
LookAhead(Move* m, Platform* p, LookAhead* n);
LookAhead* Next();
LookAhead* Previous();
void Init(float m[], float uu, float vv);
void SetUV(float uu, float vv);
float* Movement();
float U();
float V();
private:
Move* move;
Platform* platform;
LookAhead* next;
LookAhead* previous;
float Cosine(LookAhead* a);
float movement[DRIVES+1]; // Last is feedrate
float u, v;
};
class Move
{
@ -87,6 +111,10 @@ class Move
boolean DDARingFull();
boolean GetDDARingLock();
void ReleaseDDARingLock();
boolean LookAheadRingEmpty();
boolean LookAheadRingFull();
boolean LookAheadRingAdd(float m[], float uu, float vv);
LookAhead* LookAheadRingGet();
Platform* platform;
GCodes* gCodes;
@ -95,6 +123,10 @@ class Move
DDA* ddaRingAddPointer;
DDA* ddaRingGetPointer;
volatile boolean ddaRingLocked;
LookAhead* lookAheadRingAddPointer;
LookAhead* lookAheadRingGetPointer;
DDA* lookAheadDDA;
unsigned long lastTime;
boolean active;
@ -118,6 +150,37 @@ inline DDA* DDA::Next()
return next;
}
inline LookAhead* LookAhead::Next()
{
return next;
}
inline LookAhead* LookAhead::Previous()
{
return previous;
}
inline void LookAhead::SetUV(float uu, float vv)
{
u = uu;
v = vv;
}
inline float* LookAhead::Movement()
{
return movement;
}
inline float LookAhead::U()
{
return u;
}
inline float LookAhead::V()
{
return v;
}
inline boolean Move::DDARingEmpty()
{
return ddaRingGetPointer == ddaRingAddPointer;
@ -130,6 +193,18 @@ inline boolean Move::DDARingFull()
return ddaRingGetPointer->Next()->Next() == ddaRingAddPointer;
}
inline boolean Move::LookAheadRingEmpty()
{
return lookAheadRingGetPointer == lookAheadRingAddPointer;
}
// Leave a gap of 2 as the last Get result may still be being processed
inline boolean Move::LookAheadRingFull()
{
return lookAheadRingGetPointer->Next()->Next() == lookAheadRingAddPointer;
}
inline boolean Move::GetDDARingLock()
{
if(ddaRingLocked)

View file

@ -22,6 +22,7 @@ Licence: GPL
Move::Move(Platform* p, GCodes* g)
{
char i;
active = false;
platform = p;
gCodes = g;
@ -30,11 +31,28 @@ Move::Move(Platform* p, GCodes* g)
ddaRingAddPointer = new DDA(this, platform, NULL);
dda = ddaRingAddPointer;
for(char i = 1; i < RING_LENGTH; i++)
for(i = 1; i < RING_LENGTH; i++)
dda = new DDA(this, platform, dda);
ddaRingAddPointer->next = dda;
dda = NULL;
lookAheadRingAddPointer = new LookAhead(this, platform, NULL);
lookAheadRingGetPointer = lookAheadRingAddPointer;
for(i = 1; i < RING_LENGTH; i++)
lookAheadRingGetPointer = new LookAhead(this, platform, lookAheadRingGetPointer);
lookAheadRingAddPointer->next = lookAheadRingGetPointer;
lookAheadRingGetPointer = lookAheadRingAddPointer;
for(i = 0; i < RING_LENGTH; i++)
{
lookAheadRingAddPointer = lookAheadRingAddPointer->Next();
lookAheadRingAddPointer->previous = lookAheadRingGetPointer;
lookAheadRingGetPointer = lookAheadRingAddPointer;
}
lookAheadDDA = new DDA(this, platform, NULL);
}
void Move::Init()
@ -46,11 +64,13 @@ void Move::Init()
for(i = 0; i <= AXES; i++)
currentPosition[i] = 0.0;
// Empty the ring
// Empty the rings
ddaRingGetPointer = ddaRingAddPointer;
ddaRingLocked = false;
lookAheadRingGetPointer = lookAheadRingAddPointer;
// The stepDistances arrays are look-up tables of the Euclidean distance
// between the start and end of a step. If the step is just along one axis,
// it's just that axis's step length. If it's more, it is a Pythagoran
@ -227,6 +247,27 @@ void Move::Interrupt()
dda = NULL;
}
boolean Move::LookAheadRingAdd(float m[], float uu, float vv)
{
if(LookAheadRingFull())
return false;
lookAheadRingAddPointer->Init(m, uu, vv);
lookAheadRingAddPointer = lookAheadRingAddPointer->Next();
return true;
}
LookAhead* Move::LookAheadRingGet()
{
LookAhead* result;
if(LookAheadRingEmpty())
return NULL;
result = lookAheadRingGetPointer;
lookAheadRingGetPointer = lookAheadRingGetPointer->Next();
return result;
}
boolean Move::AllMovesFinished()
{
// TODO - put some code in here
@ -240,16 +281,15 @@ boolean Move::AllMovesFinished()
void Move::InterruptTime()
{
char buffer[50];
DDA* ddax = new DDA(this, platform, NULL);
float a[] = {1.0, 2.0, 3.0, 4.0, 5.0};
float b[] = {2.0, 3.0, 4.0, 5.0, 6.0};
float u = 50;
float v = 50;
ddax->Init(a, b, u, v);
ddax->Start(false);
lookAheadDDA->Init(a, b, u, v);
lookAheadDDA->Start(false);
unsigned long t = platform->Time();
for(long i = 0; i < 100000; i++)
ddax->Step(false);
lookAheadDDA->Step(false);
t = platform->Time() - t;
platform->Message(HOST_MESSAGE, "Time for 100000 calls of the interrupt function: ");
sprintf(buffer, "%ld", t);
@ -552,5 +592,51 @@ void DDA::Step(boolean noTest)
platform->SetInterrupt(STANDBY_INTERRUPT_RATE);
}
//***************************************************************************************************
LookAhead::LookAhead(Move* m, Platform* p, LookAhead* n)
{
move = m;
platform = p;
next = n;
}
void LookAhead::Init(float m[], float uu, float vv)
{
u = uu;
v = vv;
for(char i = 0; i <= DRIVES; i++)
movement[i] = m[i];
}
// This returns the cosine of the angle between
// the movement starting at start, and the movement
// starting at the end of that. Note that it
// includes Z movements, though Z values will almost always
// not change.
float LookAhead::Cosine(LookAhead* start)
{
LookAhead* n1 = start->Next();
LookAhead* n2 = n1->Next();
float sum = 0.0;
float a2 = 0.0;
float b2 = 0.0;
float m1;
float m2;
for(char i = 0; i < AXES; i++)
{
m1 = n1->movement[i] - start->movement[i];
m2 = n2->movement[i] - n1->movement[i];
a2 += m1*m1;
b2 += m2*m2;
sum += m1*m2;
}
sum = sum/( (float)sqrt(a2) * (float)sqrt(b2) );
return sum;
}

View file

@ -7,6 +7,7 @@ RepRap self-replicating 3D printers.
It owes a lot to Marlin and to the original RepRap FiveD_GCode.
General design principles:
* Control by RepRap G Codes. These are taken to be machine independent, though some may be unsupported.
@ -23,6 +24,76 @@ General design principles:
* Don't avoid arrays and structs/classes,
* Don't avoid pointers,
* Use operator and function overloading where appropriate, particularly for vector algebra.
Naming conventions:
* #defines are all capitals with optional underscores between words
* No underscores in other names - make readable with capitalisation: MakeReadableWithCapitalisation
* Classes and functions start with a capital letter
* Variables start with a lower case letter
Structure:
There are six main classes:
* RepRap
* GCodes
* Heat
* Move
* Platform, and
* Webserver
RepRap:
This is just a container class for the single instances of all the others, and otherwise does very little.
GCodes:
This class is fed GCodes, either from the web interface or from GCode files, interprests them, and requests
actions from the RepRap machine via the other classes.
Heat:
This class imlements all heating and temperature control in the RepRap machine.
Move:
This class controls all movement of the RepRap machine, both along its axes, and in its extruder drives.
Platform:
This is the only class that knows anything about the physical setup of the RepRap machine and its
controlling electronics. It implements the interface between all the other classes and the RepRap machine.
All the other classes are completely machine-independent (though they may declare arrays dimensioned
to values #defined in Platform.h).
Webserver:
This class talks to the network (via Platform) and implements a simple webserver to give an interactive
interface to the RepRap machine. It uses the Knockout and Jquery Javascript libraries to achieve this.
When the software is running there is one single instance of each class, and all the memory allocation is
done on initialisation. new/malloc should not be used in the general running code, and delete is never
used. Each class has an Init() function that resets it to its boot-up state; the constructors merely handle
that memory allocation on startup. Calling RepRap.Init() calls all the other Init()s in the right sequence.
There are other ancilliary classes that are declared in the .h files for the master classes that use them. For
example, Move has a DDA class that implements a Bresenham/digital differential analyser.
All the main classes have a Spin() function. These are called in a loop by the RepRap.Spin() function and implement
simple timesharing. No class does, or ever should, wait inside one of its functions for anything to happen or call
any sort of delay() function. The general rule is:
Can I do a thing?
Yes - do it
No - set a flag/timer to remind me to do it next-time-I'm-called/at-a-future-time and return.
Note that it is simple to raise the "priority" of any class's activities relative to the others by calling its
Spin() function more than once from RepRap.Spin().
-----------------------------------------------------------------------------------------------------