Started to add code to implement look-ahead.
This commit is contained in:
parent
438737f2f2
commit
0c77a2f5eb
6 changed files with 280 additions and 31 deletions
|
@ -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
|
||||
|
|
22
GCodes.h
22
GCodes.h
|
@ -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
|
||||
|
|
41
GCodes.ino
41
GCodes.ino
|
@ -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
75
Move.h
|
@ -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)
|
||||
|
|
98
Move.ino
98
Move.ino
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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().
|
||||
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
Reference in a new issue