1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
/*
* GCodeInput.cpp
*
* Created on: 16 Sep 2016
* Author: Christian
*/
#include "GCodeInput.h"
#include "RepRap.h"
#include "GCodes.h"
#include "GCodeBuffer/GCodeBuffer.h"
// Read some input bytes into the GCode buffer. Return true if there is a line of GCode waiting to be processed.
bool StandardGCodeInput::FillBuffer(GCodeBuffer *gb) noexcept
{
const size_t bytesToPass = min<size_t>(BytesCached(), GCODE_LENGTH);
for (size_t i = 0; i < bytesToPass; i++)
{
const char c = ReadByte();
#if HAS_MASS_STORAGE
if (gb->IsWritingBinary())
{
// HTML uploads are handled by the GCodes class
gb->WriteBinaryToFile(c);
}
else if (gb->Put(c))
{
if (gb->IsWritingFile())
{
gb->WriteToFile();
}
else
{
return true;
}
}
#else
if (gb->Put(c))
{
// Code is complete, so stop here
return true;
}
#endif
}
return false;
}
// G-code input class for wrapping around Stream-based hardware ports
void StreamGCodeInput::Reset() noexcept
{
while (device.available() > 0)
{
device.read();
}
}
char StreamGCodeInput::ReadByte() noexcept
{
return static_cast<char>(device.read());
}
size_t StreamGCodeInput::BytesCached() const noexcept
{
return device.available();
}
// Dynamic G-code input class for caching codes from software-defined sources
RegularGCodeInput::RegularGCodeInput() noexcept
: state(GCodeInputState::idle), writingPointer(0), readingPointer(0)
{
}
void RegularGCodeInput::Reset() noexcept
{
state = GCodeInputState::idle;
writingPointer = readingPointer = 0;
}
char RegularGCodeInput::ReadByte() noexcept
{
char c = buffer[readingPointer++];
if (readingPointer == GCodeInputBufferSize)
{
readingPointer = 0;
}
return c;
}
size_t RegularGCodeInput::BytesCached() const noexcept
{
if (writingPointer >= readingPointer)
{
return writingPointer - readingPointer;
}
return GCodeInputBufferSize - readingPointer + writingPointer;
}
size_t RegularGCodeInput::BufferSpaceLeft() const noexcept
{
return (readingPointer - writingPointer - 1u) % GCodeInputBufferSize;
}
void NetworkGCodeInput::Put(MessageType mtype, char c) noexcept
{
if (BufferSpaceLeft() == 0)
{
// Don't let the buffer overflow if we run out of space
return;
}
// Check for M112 (emergency stop) and for M122 (diagnostics) while receiving new characters
switch (state)
{
case GCodeInputState::idle:
if (c <= ' ')
{
// ignore whitespaces at the beginning
return;
}
state = (c == 'M' || c == 'm') ? GCodeInputState::doingMCode : GCodeInputState::doingCode;
break;
case GCodeInputState::doingCode:
if (c == 0 || c == '\r' || c == '\n')
{
state = GCodeInputState::idle;
}
break;
case GCodeInputState::doingMCode:
state = (c == '1') ? GCodeInputState::doingMCode1 : GCodeInputState::doingCode;
break;
case GCodeInputState::doingMCode1:
state = (c == '1') ? GCodeInputState::doingMCode11: (c == '2') ? GCodeInputState::doingMCode12 : GCodeInputState::doingCode;
break;
case GCodeInputState::doingMCode11:
state = (c == '2') ? GCodeInputState::doingMCode112 : GCodeInputState::doingCode;
break;
case GCodeInputState::doingMCode12:
state = (c == '2') ? GCodeInputState::doingMCode122 : GCodeInputState::doingCode;
break;
case GCodeInputState::doingMCode112:
if (c <= ' ' || c == ';')
{
// Emergency stop requested - perform it now
reprap.EmergencyStop();
reprap.GetGCodes().Reset();
// But don't run it twice
Reset();
return;
}
state = GCodeInputState::doingCode;
break;
case GCodeInputState::doingMCode122:
// Only execute M122 here if there is no parameter
if (c < ' ' || c == ';')
{
// Diagnostics requested - report them now
// Don't use the Network task itself to send them because this may result in deadlock if there is insufficient buffer space,
// because the Network task itself is used to send the output to the clients and free the buffers.
// Ask the main task to do it instead.
reprap.DeferredDiagnostics(mtype);
// But don't report them twice
Reset();
return;
}
state = GCodeInputState::doingCode;
break;
}
// Feed another character into the buffer
buffer[writingPointer++] = c;
if (writingPointer == GCodeInputBufferSize)
{
writingPointer = 0;
}
}
void NetworkGCodeInput::Put(MessageType mtype, const char *buf) noexcept
{
const size_t len = strlen(buf) + 1;
MutexLocker lock(bufMutex, 200);
if (lock)
{
// Only cache this if we have enough space left
if (len <= BufferSpaceLeft())
{
for (size_t i = 0; i < len; i++)
{
Put(mtype, buf[i]);
}
}
}
}
NetworkGCodeInput::NetworkGCodeInput() noexcept : RegularGCodeInput()
{
bufMutex.Create("NetworkGCodeInput");
}
// Fill a GCodeBuffer with the last available G-code
bool NetworkGCodeInput::FillBuffer(GCodeBuffer *gb) noexcept /*override*/
{
MutexLocker lock(bufMutex, 10);
return lock && RegularGCodeInput::FillBuffer(gb);
}
#if HAS_MASS_STORAGE
// File-based G-code input source
// Reset this input. Should be called when the associated file is being closed
void FileGCodeInput::Reset() noexcept
{
lastFile = nullptr;
RegularGCodeInput::Reset();
}
// Reset this input. Should be called when a specific G-code or macro file is closed outside of the reading context
void FileGCodeInput::Reset(const FileData &file) noexcept
{
if (file.f == lastFile)
{
Reset();
}
}
// Read another chunk of G-codes from the file and return true if more data is available
GCodeInputReadResult FileGCodeInput::ReadFromFile(FileData &file) noexcept
{
const size_t bytesCached = BytesCached();
// Keep track of the last file we read from
if (lastFile != nullptr && lastFile != file.f)
{
if (bytesCached > 0)
{
// Rewind back to the right position so we can resume at the right position later.
// This may be necessary when nested macros are executed.
lastFile->Seek(lastFile->Position() - bytesCached);
}
RegularGCodeInput::Reset();
}
lastFile = file.f;
// Read more from the file
if (bytesCached < GCodeInputFileReadThreshold)
{
// Reset the read+write pointers for better performance if possible
if (readingPointer == writingPointer)
{
readingPointer = writingPointer = 0;
}
// The code here used to read into a local buffer in blocks that are multiples of 4 bytes.
// However, unless we can use a buffer of at least 512 bytes then that is redundant,
// because the data will be copied via the sector buffer in FatFS anyway. So we don't do that any more.
const int bytesRead = file.Read(buffer + writingPointer, min<size_t>(BufferSpaceLeft(), GCodeInputBufferSize - writingPointer));
if (bytesRead < 0)
{
return GCodeInputReadResult::error;
}
if (bytesRead > 0)
{
writingPointer = (writingPointer + (size_t)bytesRead) % GCodeInputBufferSize;
return GCodeInputReadResult::haveData;
}
}
return (bytesCached > 0) ? GCodeInputReadResult::haveData : GCodeInputReadResult::noData;
}
#endif
// End
|