-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
telnet.canReadLine()) #3
Comments
Nop. What you must do is to read and store incomming data (in your slot connected to newData signal) and when a new line is present (or whatever you want) call your functions. Your approach is fine when there are two threads: your code and socked code. So you need to call "canReadLine" on socked code. Not needed in Qt framework. Tell me if you need help to implement this properly. |
Hello, greetings from Nicaragua. thanks for your answer. I am not an expert programmer, but I am implementing a telnet (your library) communication between hardware and software. The program is based on sending and receiving data. in serial communication there is no problem. void frmMain::sendCommand(QString command, int tableIndex, bool showInConsole)
qDebug() << "sendata:" << command; //m_serialPort.write((command + "\r").toLatin1()); telnet.sendData((command + "\r").toLatin1().trimmed()); 2- Recieving data //connect(&m_serialPort, SIGNAL(readyRead()), this, SLOT(onSerialPortReadyRead()), Qt::AutoConnection); connect( &telnet, SIGNAL(newData(const char*,int)), this, SLOT(onTelnetPortReadyRead(const char*,int)) ); //void frmMain::onSerialPortReadyRead()
// QString data = m_serialPort.readLine().trimmed();
} In serial communication I receive formatted data: It's the right way to operate. Recieveddata: "ok" In telnet communication I receive this data: Recieveddata: "ok" Recieveddata: "[G54:-83.285,23.397,4.872,-40.000,0.000,0.000]\n[G55:0.000,0.000,0.000,0.000,0.000,0.000]\n[G56:0.000,0.000,0.000,0.000,0.000,0.000]\n[G57:0.000,0.000,0.000,0.000,0.000,0.000]\n[G58:0.000,0.000,0.000,0.000,0.000,0.000]\n[G59:0.000,0.000,0.000,0.000,0.000,0.000]\n[G28:-334.875,52.796,0.000,0.000,-137.182,-137.182]\n[G30:-334.875,52.796,0.000,0.000,-137.182,-137.182]\n[G92:35.158,16.739,0.000,0.000,0.000,0.000]\n[TLO:0.000]\nok" this generates some bugs for me. I don't know if it's something in this part of the code void QTelnet::onReadyRead()
} or I'm the problem in this part. QByteArray data(msg, count); thank you very much for your help. |
¿De Nicaragua? Hablas español, supongo.
Es un poco tarde aquí en España. Así que igual lo que te digo algún
sinsentido ;)
La manera que implementas onTelnetPortReadyRead tienen un pequeño error
(aunque creo que no te afectará)
Fíjate en cómo implemento este mismo slot en el fichero QTelnetTester.cpp,
en la función addText(...)
La función addText lo que hace es añadir el texto que llega desde telnet a
un visor de texto.
Veras que, simplemente, coge el buffer, lo envía al QWidget y luego fuerza
el QScrollBar a ir abajo del todo para que se muestre lo que ha llegado.
Bien, con esto quiero decirte que no necesitas comprobar la variable count.
Si vas al fichero QTelnet.cpp verás que ahí dentro ya se comprueba que
"count" sea válido y, por lo tanto, no necesitas comprobarlo tú.
Aun así, no está de más hacer esta comprobación porque no siempre
conoces/ves el código fuente de la API.
PEEEERO debe ser if( count > 0 )
Y NO if( count >1 )
No creo que esto te haya dado problemas: sería rarísimo que sólo te llegara
1 byte desde Telnet. Pero lo que está bien, está bien ;)
Luego, creo que el único problema que tienes es que debes separar lo que te
ha llegado por Telnet en las diferentes líneas.
Prueba esto en tu slot onTelnetPortReadyRead:
QByteArray data(msg, count);
foreach( const QByteArray &lineaByes : data.split('\n') )
{
QString lineaString = data.toStdString();
qDebug() << "Receiveddata: " << lineaString << "\n";
}
También, debes tener siempre presente que este código no espera a que haya
líneas completas. Por lo tanto, podrías recibir un [Gxxxxxxxx] en dos
invocaciones distintas de tu Slot.
Cuando lees los datos del puerto serie, internamente, la función
"canReadLine" sólo retornará true cuando realmente hay una línea ENTERA.
Pero el código que he puesto antes, no.
Habrá varias maneras de hacerlo. Se me ocurre esta:
QByteArray data(msg, count);
static QByteArray bytesLinea;
for( int i = 0; i < count; ++i )
{
if( ((msg[i] == '\n') || (msg[i] == '\r')) && !bytesLinea.isEmpty() )
{
qDebug() << "Receiveddata: " << bytesLinea.toStdString() << "\n" ;
// DO your stuff here.
bytesLinea.clear();
}
}
No he probado nada y llevo mucho tiempo sin tocar las librerías Qt. Así que
revisa que no haya puesto una tontería.
¡Ya me cuentas qué tal!
Un saludo,
Rafa.
|
Hola, gracias por su respuesta y amabilidad, si hablo español jeje. La verdad estoy tratando de hacer funcionar este proyecto https://github.com/Denvi/Candle, pero con comunicion telnet. No se, si usted ha escuchado de grbl. Simplemente es un proyecto para menejo de equipos CNC de codigo abierto. Pero hasta el momento solo hay una opción por telnet o websocket, pero no es de mi agrado. Así que decidí trabajar este proyecto que para mí es muy comodo y agradable visualmente. Entonces encontré su librería para telnet y la implementé en el candle. Funciona bien, pero mi problema es a la hora de recibir datos, no me interpreta las "\n" como su debido siguiente línea. cuando la aplicación se conecta por telnet (con lo que he modificado el codigo) recibo exactamente la respuesta a como debería estar en serial. grbl reset y asi esta repitiendo esta secuencia una vez se envia "$G". hasta por aca todo espectacular, el problema ocurré si yo envio un seteo de posición. sendata: "G92X0Y0" el envio de dato esta a como debería ser, pero al recibir el dato recibo el mensaje sin su debido espacio, como que no interpreta el \n. y esto genera que no se setee o que la tarjeta al comparar estos valores no lo haga adecuadamente. Pero si yo envio un archivo para que lo procese, se procesa muy bien, aunque a veces recibe ese \n y crashea. Ayer estuve probando sus lineas. QByteArray data(msg, count); foreach( const QByteArray &lineaByes : data.split('\n') ) En esta parte el compildor me da un error, no sabe quien es &lineaByes. QByteArray data(msg, count); static QByteArray bytesLinea; for( int i = 0; i < count; ++i ) en este otro funciona, pero lo recibe sin comillas y a la hora de poner el resto de codigo en // DO your stuff here// no funciona. Con respecto a esto: "PEEEERO debe ser if( count > 0 ) Y NO if( count >1 )", si pongo cero no funciona. Es extraño, pero no funciona. Aclaro, no creo sea un problema con su librería, mas bien parece un problema de que no he podido procesar bien lo que recibo. |
No me dedico a la programación a nivel profesional. Aunque tengo algunos
programas hechos para mi trabajo.
No me importa ayudar. Aunque, lamentablemente, no tengo el hardware
necesario para simular el CNC en Candle.
¿Lo que pretendes es modificar Candle para que use mi librería en la
comunicación con el CNC en vez del "serial port"?
Cuando dices "lo recibe sin comillas"... ¿Podrías explicarme más detalles?
La verdad es que me parece rarísimo ver el texto "\n". Y sólo cuando le
indicas al CNC que vaya a una posición y no con el reset. ¿Te he entendido
bien?
El error es que "foreach" no se usa como lo puse yo... es " foreach( const
QByteArray &lineaByes, data.split('\n') )" O mejor (más estándar): for(
const QByteArray &lineaByes : data.split('\n') )"Llevo mucho sin tocar C++
ni Qt :P
Por otro lado, podrías crear un fichero binario con los datos recibidos, me
lo pasas y lo abro. Con ese fichero quizá pueda echarte una mano.
Para crear un fichero que se cree solo una vez por sesión del programa, es
algo así:
QFile static f("out.data");
static bool Abierto = false;
if( !Abierto )
Abierto = f.open(QIODevice::WriteOnly);
f.write(data, count);
f.flush();
Luego (o mañana) le echaré un vistazo al código del Candle. A ver si
descubro algo más.
|
Si , con el harware es de evidente ayuda. Si estoy modificando el candle, y a como le dije funciona bien, solo con los inconvenientes mencionados. De hecho ya está agregada su libreria al proyecto... Asi es, con el reset esta funcionando bien, pero a la hora de dar algún comando en el que el grbl retorna posiciones , es ahi los problemas de los \n, de ahi con otros comando que no retorne posiciones no hay problema. Voy a revisar para crear el fichero. De todas formas el hardaware es un ESP32 (https://www.ebay.es/itm/364306536290?itmmeta=01HZDT4RR5GKXP67YXDRX6KZ9R&hash=item54d25c9762:g:2~YAAOSw1ZVkjWEQ&itmprp=enc%3AAQAJAAAA4JFj9dxzyXUzYlC0rrtMedok4%2BebVZMv6G9rZSUlx%2BK3kCW6SIJ4eAOEv%2FA1P6OPYz3eT%2BRbj0rAhNzwqpC5f--L56xnPUrJFU%2B1DfNqt5pJs08N3auznox615DSj7emcKzCu%2BM3gnBW%2F9%2BvCZm%2FBjUb2UVRqrKsYmZ9cZ4jbgIRXpumm9DNMdQXxYbXK6TrrQuG8RFQyib72Iz0uBRcmkPvl4WGGkxD3Uk4BIsbmZUvnF4aO1Kerx6VNrEgAyLUyioFNCeVO78q0yTuAWqodcpAIM8hB3WyUCLUbsaDu5Si%7Ctkp%3ABk9SR5aMk7r7Yw) con el firmware Fluid cnc https://github.com/bdring/FluidNC Por otro lado, con su mismo programa Qtelnettester se puede estar probando, ya que el debug retorna los mismo errores. El debug debe estar sin <<\n, ya que deberia recibir los datos ya con su respectivo "enter". |
Muchas gracias. Funcionaaa... for(const QByteArray &lineaByes : data.split('\n') ), ayudo mucho, ya no tiene ningun bug, ni con altas velocidades. Su librería esta genial y ahora con su ayuda ya se tiene un controlador CNC por telnet. Asi que su libreria andará gestionando placas CNC por telnet. Igual seguiré depurando, pero muchas gracias. Ahora solo necesito probarla con otro firmware y estaría completo. |
Me alegro ;)
Si quieres ayuda para hacer un "fork" del proyecto original y añadir la
posibilidad de comunicación a través de Telnet que sea elegible desde la
GUI del programa, en vez de las modificaciones que has hecho
"hardcodeadas", dímelo. Aunque ando un poco oxidado en Qt.... :P
Da la casualidad que tengo un par de proyectos con ESP32 XD. Pero bueno, no
me vale la pena meterme en ello. Y menos sabiendo que ahora te funciona.
Por cierto, aunque te haya funcionado hacer el "split" de los datos que han
llegado, deberías buscar por qué no va el último código que te pase.
Si por el motivo que sea recibes media línea, tendrás dos medias líneas, en
dos invocaciones del slot y, por lo tanto, datos corruptos.
Los paquetes de datos por la red son de 1500 bytes. Quitando la cabecera IP
y el protocolo Telnet, igual te quedan 1400 bytes "usables" (me lo invento).
En esos 1400 bytes el CNC tiene de sobra para enviar las respuestas. Es
más. Creo que cada vez que se invoca el slot de newData, tendrá una sola
línea, ya que, por muy rápida que sea la máquina de control numérico, no lo
será tanto como para hacer tantas acciones que sobrepasen la capacidad de
los paquetes Ethernet.
Aun así, llámalo purismo de programador, es mejor no asumir nada y dejarlo
todo atado.
He estado mirando el código de Candle. Justamente donde está el
slot onSerialPortReadyRead es un código muy obscuro y poco mantenible:
mezcla la parte de comunicación con la parte de análisis del texto que
llega por el puerto serie. Esto hace que añadir la funcionalidad de Telnet
no sea limpio. Por eso has tenido que modificar los parámetros que pasas a
las funciones para adaptarlas a Telnet. Lo ideal sería, como te comenté,
mantener las dos opciones de comunicación; que el usuario decida cómo
conectarse. Prueba esto:
void frmMain::onSerialPortReadyRead()
{
while( m_serialPort.canReadLine() )
parseReceivedLine(m_serialPort.readLine().trimmed());
}
#define USE_SPLIT_TEXT
void frmMain::onTelnetPortReadyRead(const char *data, int count)
{
#ifdef USE_SPLIT_TEXT
for( const QByteArray &linea : QByteArray(data, count).split('\n') )
parseReceivedLine(linea.toStdString()).trimmed();
#else
static QByteArray bytesLinea;
for( int i = 0; i < count; ++i )
{
if( ((msg[i] == '\n') || (msg[i] == '\r')) && !bytesLinea.isEmpty()
)
{
parseReceivedLine(linea.toStdString.trimmed());
bytesLinea.clear();
}
}
#endif
}
Añade la función parseReceivedLine(QString data) en la definición de la
clase frmMain y modifica la actual onSerialPortReadyRead quedando así:
void frmMain::parseReceivedLine(QString data)
{
// Filter prereset responses
if (m_reseting) {
qDebug() << "reseting filter:" << data;
if (!dataIsReset(data)) continue;
else {
m_reseting = false;
m_timerStateQuery.setInterval(m_settings->queryStateTime());
}
}
// Status response
if (data[0] == '<') {
int status = -1;
m_statusReceived = true;
// Update machine coordinates
static QRegExp mpx("MPos:([^,]*),([^,]*),([^,^>^|]*)");
if (mpx.indexIn(data) != -1) {
ui->txtMPosX->setText(mpx.cap(1));
ui->txtMPosY->setText(mpx.cap(2));
ui->txtMPosZ->setText(mpx.cap(3));
}
// Status
static QRegExp stx("<([^,^>^|]*)");
if (stx.indexIn(data) != -1) {
status = m_status.indexOf(stx.cap(1));
// Undetermined status
if (status == -1) status = 0;
// Update status
if (status != m_lastGrblStatus) {
ui->txtStatus->setText(m_statusCaptions[status]);
ui->txtStatus->setStyleSheet(QString("background-color: %1;
color: %2;")
.arg(m_statusBackColors
[status]).arg(m_statusForeColors[status]));
}
// Update controls
ui->cmdRestoreOrigin->setEnabled(status == IDLE);
ui->cmdSafePosition->setEnabled(status == IDLE);
ui->cmdZeroXY->setEnabled(status == IDLE);
ui->cmdZeroZ->setEnabled(status == IDLE);
ui->chkTestMode->setEnabled(status != RUN && !m_processingFile);
ui->chkTestMode->setChecked(status == CHECK);
ui->cmdFilePause->setChecked(status == HOLD0 || status == HOLD1
|| status == QUEUE);
ui->cmdSpindle->setEnabled(!m_processingFile || status ==
HOLD0);
#ifdef WINDOWS
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
if (m_taskBarProgress) m_taskBarProgress->setPaused(status
== HOLD0 || status == HOLD1 || status == QUEUE);
}
#endif
// Update "elapsed time" timer
if (m_processingFile) {
QTime time(0, 0, 0);
int elapsed = m_startTime.elapsed();
ui->glwVisualizer->setSpendTime(time.addMSecs(elapsed));
}
// Test for job complete
if (m_processingFile && m_transferCompleted &&
((status == IDLE && m_lastGrblStatus == RUN) || status
== CHECK)) {
qDebug() << "job completed:" << m_fileCommandIndex <<
m_currentModel->rowCount() - 1;
// Shadow last segment
GcodeViewParse *parser = m_currentDrawer->viewParser();
QList<LineSegment*> list = parser->getLineSegmentList();
if (m_lastDrawnLineIndex < list.count()) {
list[m_lastDrawnLineIndex]->setDrawn(true);
m_currentDrawer->update(QList<int>() <<
m_lastDrawnLineIndex);
}
// Update state
m_processingFile = false;
m_fileProcessedCommandIndex = 0;
m_lastDrawnLineIndex = 0;
m_storedParserStatus.clear();
updateControlsState();
qApp->beep();
m_timerStateQuery.stop();
m_timerConnection.stop();
QMessageBox::information(this, qApp->applicationDisplayName(),
tr("Job done.\nTime elapsed: %1")
.arg(ui->glwVisualizer->
spendTime().toString("hh:mm:ss")));
m_timerStateQuery.setInterval(m_settings->queryStateTime());
m_timerConnection.start();
m_timerStateQuery.start();
}
// Store status
if (status != m_lastGrblStatus) m_lastGrblStatus = status;
// Abort
static double x = sNan;
static double y = sNan;
static double z = sNan;
if (m_aborting) {
switch (status) {
case IDLE: // Idle
if (!m_processingFile && m_resetCompleted) {
m_aborting = false;
restoreOffsets();
restoreParserState();
return;
}
break;
case HOLD0: // Hold
case HOLD1:
case QUEUE:
if (!m_reseting && compareCoordinates(x, y, z)) {
x = sNan;
y = sNan;
z = sNan;
grblReset();
} else {
x = ui->txtMPosX->text().toDouble();
y = ui->txtMPosY->text().toDouble();
z = ui->txtMPosZ->text().toDouble();
}
break;
}
}
}
// Store work offset
static QVector3D workOffset;
static QRegExp wpx("WCO:([^,]*),([^,]*),([^,^>^|]*)");
if (wpx.indexIn(data) != -1)
{
workOffset = QVector3D(wpx.cap(1).toDouble(), wpx.cap(2).
toDouble(), wpx.cap(3).toDouble());
}
// Update work coordinates
int prec = m_settings->units() == 0 ? 3 : 4;
ui->txtWPosX->setText(QString::number(ui->txtMPosX->text().toDouble()
- workOffset.x(), 'f', prec));
ui->txtWPosY->setText(QString::number(ui->txtMPosY->text().toDouble()
- workOffset.y(), 'f', prec));
ui->txtWPosZ->setText(QString::number(ui->txtMPosZ->text().toDouble()
- workOffset.z(), 'f', prec));
// Update tool position
QVector3D toolPosition;
if (!(status == CHECK && m_fileProcessedCommandIndex <
m_currentModel->rowCount() - 1)) {
toolPosition = QVector3D(toMetric(ui->txtWPosX->text().toDouble
()),
toMetric(ui->txtWPosY->text().
toDouble()),
toMetric(ui->txtWPosZ->text().
toDouble()));
m_toolDrawer.setToolPosition(m_codeDrawer->getIgnoreZ() ?
QVector3D(toolPosition.x(), toolPosition.y(), 0) : toolPosition);
}
// toolpath shadowing
if (m_processingFile && status != CHECK) {
GcodeViewParse *parser = m_currentDrawer->viewParser();
bool toolOntoolpath = false;
QList<int> drawnLines;
QList<LineSegment*> list = parser->getLineSegmentList();
for (int i = m_lastDrawnLineIndex; i < list.count()
&& list.at(i)->getLineNumber()
<=
(m_currentModel->data(m_currentModel->index(m_fileProcessedCommandIndex,
4)).toInt() + 1); i++) {
if (list.at(i)->contains(toolPosition)) {
toolOntoolpath = true;
m_lastDrawnLineIndex = i;
break;
}
drawnLines << i;
}
if (toolOntoolpath) {
foreach (int i, drawnLines) {
list.at(i)->setDrawn(true);
}
if (!drawnLines.isEmpty()) m_currentDrawer->update
(drawnLines);
} else if (m_lastDrawnLineIndex < list.count()) {
qDebug() << "tool missed:" << list.at
(m_lastDrawnLineIndex)->getLineNumber()
<<
m_currentModel->data(m_currentModel->index(m_fileProcessedCommandIndex,
4)).toInt()
<< m_fileProcessedCommandIndex;
}
}
// Get overridings
static QRegExp ov("Ov:([^,]*),([^,]*),([^,^>^|]*)");
if (ov.indexIn(data) != -1)
{
updateOverride(ui->slbFeedOverride, ov.cap(1).toInt(), 0x91);
updateOverride(ui->slbSpindleOverride, ov.cap(3).toInt(), 0x9a);
int rapid = ov.cap(2).toInt();
ui->slbRapidOverride->setCurrentValue(rapid);
int target = ui->slbRapidOverride->isChecked() ? ui->
slbRapidOverride->value() : 100;
if (rapid != target) switch (target) {
case 25:
m_serialPort.write(QByteArray(1, char(0x97)));
break;
case 50:
m_serialPort.write(QByteArray(1, char(0x96)));
break;
case 100:
m_serialPort.write(QByteArray(1, char(0x95)));
break;
}
// Update pins state
QString pinState;
static QRegExp pn("Pn:([^|^>]*)");
if (pn.indexIn(data) != -1) {
pinState.append(QString(tr("PS: %1")).arg(pn.cap(1)));
}
// Process spindle state
static QRegExp as("A:([^,^>^|]+)");
if (as.indexIn(data) != -1) {
QString state = as.cap(1);
m_spindleCW = state.contains("S");
if (state.contains("S") || state.contains("C")) {
m_timerToolAnimation.start(25, this);
ui->cmdSpindle->setChecked(true);
} else {
m_timerToolAnimation.stop();
ui->cmdSpindle->setChecked(false);
}
if (!pinState.isEmpty()) pinState.append(" / ");
pinState.append(QString(tr("AS: %1")).arg(as.cap(1)));
} else {
m_timerToolAnimation.stop();
ui->cmdSpindle->setChecked(false);
}
ui->glwVisualizer->setPinState(pinState);
}
// Get feed/spindle values
static QRegExp fs("FS:([^,]*),([^,^|^>]*)");
if (fs.indexIn(data) != -1) {
ui->glwVisualizer->setSpeedState((QString(tr("F/S: %1 / %2")).
arg(fs.cap(1)).arg(fs.cap(2))));
}
} else if (data.length() > 0) {
// Processed commands
if (m_commands.length() > 0 && !dataIsFloating(data)
&& !(m_commands[0].command != "[CTRL+X]" && dataIsReset(data)))
{
static QString response; // Full response string
if ((m_commands[0].command != "[CTRL+X]" && dataIsEnd(data))
|| (m_commands[0].command == "[CTRL+X]" &&
dataIsReset(data)))
{
response.append(data);
// Take command from buffer
CommandAttributes ca = m_commands.takeFirst();
QTextBlock tb = ui->txtConsole->document()->
findBlockByNumber(ca.consoleIndex);
QTextCursor tc(tb);
// Restore absolute/relative coordinate system after jog
if (ca.command.toUpper() == "$G" && ca.tableIndex == -2) {
if (ui->chkKeyboardControl->isChecked())
m_absoluteCoordinates = response.contains("G90");
else if (response.contains("G90")) sendCommand("G90", -1,
m_settings->showUICommands());
}
// Jog
if (ca.command.toUpper().contains("$J=") && ca.tableIndex ==
-2) {
jogStep();
}
// Process parser status
if (ca.command.toUpper() == "$G" && ca.tableIndex == -3) {
// Update status in visualizer window
ui->glwVisualizer->setParserStatus(response.left(
response.indexOf("; ")));
// Store parser status
if (m_processingFile) storeParserState();
// Spindle speed
QRegExp rx(".*S([\\d\\.]+)");
if (rx.indexIn(response) != -1) {
double speed = toMetric(rx.cap(1).toDouble());
//RPM in imperial?
ui->slbSpindle->setCurrentValue(speed);
}
m_updateParserStatus = true;
}
// Store origin
if (ca.command == "$#" && ca.tableIndex == -2) {
qDebug() << "Received offsets:" << response;
QRegExp rx(".*G92:([^,]*),([^,]*),([^\\]]*)");
if (rx.indexIn(response) != -1) {
if (m_settingZeroXY) {
m_settingZeroXY = false;
m_storedX = toMetric(rx.cap(1).toDouble());
m_storedY = toMetric(rx.cap(2).toDouble());
} else if (m_settingZeroZ) {
m_settingZeroZ = false;
m_storedZ = toMetric(rx.cap(3).toDouble());
}
ui->cmdRestoreOrigin->setToolTip(QString(tr("Restore
origin:\n%1, %2, %3")).arg(m_storedX).arg(m_storedY).arg(m_storedZ));
}
}
// Homing response
if ((ca.command.toUpper() == "$H" || ca.command.toUpper() ==
"$T") && m_homing) m_homing = false;
// Reset complete
if (ca.command == "[CTRL+X]") {
m_resetCompleted = true;
m_updateParserStatus = true;
}
// Clear command buffer on "M2" & "M30" command (old
firmwares)
if ((ca.command.contains("M2") || ca.command.contains("M30"))
&& response.contains("ok") && !response.contains("[Pgm End]")) {
m_commands.clear();
m_queue.clear();
}
// Process probing on heightmap mode only from table
commands
if (ca.command.contains("G38.2") && m_heightMapMode && ca.
tableIndex > -1) {
// Get probe Z coordinate
// "[PRB:0.000,0.000,0.000:0];ok"
QRegExp rx(".*PRB:([^,]*),([^,]*),([^]^:]*)");
double z = qQNaN();
if (rx.indexIn(response) != -1) {
qDebug() << "probing coordinates:" << rx.cap(1) <<
rx.cap(2) << rx.cap(3);
z = toMetric(rx.cap(3).toDouble());
}
static double firstZ;
if (m_probeIndex == -1) {
firstZ = z;
z = 0;
} else {
// Calculate delta Z
z -= firstZ;
// Calculate table indexes
int row = trunc(m_probeIndex / m_heightMapModel.
columnCount());
int column = m_probeIndex - row * m_heightMapModel.
columnCount();
if (row % 2) column = m_heightMapModel.columnCount()
- 1 - column;
// Store Z in table
m_heightMapModel.setData(m_heightMapModel.index(row,
column), z, Qt::UserRole);
ui->tblHeightMap->update(m_heightMapModel.index(
m_heightMapModel.rowCount() - 1 - row, column));
updateHeightMapInterpolationDrawer();
}
m_probeIndex++;
}
// Change state query time on check mode on
if (ca.command.contains(QRegExp("$[cC]"))) {
m_timerStateQuery.setInterval(response.contains("Enable")
? 1000 : m_settings->queryStateTime());
}
// Add response to console
if (tb.isValid() && tb.text() == ca.command) {
bool scrolledDown = ui->txtConsole->verticalScrollBar
()->value() == ui->txtConsole->verticalScrollBar()->maximum();
// Update text block numbers
int blocksAdded = response.count("; ");
if (blocksAdded > 0) for (int i = 0; i < m_commands.
count(); i++) {
if (m_commands[i].consoleIndex != -1) m_commands[i].
consoleIndex += blocksAdded;
}
tc.beginEditBlock();
tc.movePosition(QTextCursor::EndOfBlock);
tc.insertText(" < " + QString(response).replace("; ", "
\r\n"));
tc.endEditBlock();
if (scrolledDown) ui->txtConsole->verticalScrollBar()->
setValue(ui->txtConsole->verticalScrollBar()->maximum());
}
// Check queue
if (m_queue.length() > 0) {
CommandQueue cq = m_queue.takeFirst();
while ((bufferLength() + cq.command.length() + 1) <=
BUFFERLENGTH) {
sendCommand(cq.command, cq.tableIndex, cq.
showInConsole);
if (m_queue.isEmpty()) break; else cq = m_queue.
takeFirst();
}
}
// Add response to table, send next program commands
if (m_processingFile) {
// Only if command from table
if (ca.tableIndex > -1) {
m_currentModel->setData(m_currentModel->index(ca.
tableIndex, 2), GCodeItem::Processed);
m_currentModel->setData(m_currentModel->index(ca.
tableIndex, 3), response);
m_fileProcessedCommandIndex = ca.tableIndex;
if (ui->chkAutoScroll->isChecked() && ca.tableIndex
!= -1) {
ui->tblProgram->scrollTo(m_currentModel->index(
ca.tableIndex + 1, 0)); // TODO: Update by timer
ui->tblProgram->setCurrentIndex(m_currentModel->
index(ca.tableIndex, 1));
}
}
// Update taskbar progress
#ifdef WINDOWS
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7)
{
if (m_taskBarProgress) m_taskBarProgress->setValue
(m_fileProcessedCommandIndex);
}
#endif
// Process error messages
static bool holding = false;
static QString errors;
if (ca.tableIndex > -1 && response.toUpper().contains(
"ERROR") && !m_settings->ignoreErrors()) {
errors.append(QString::number(ca.tableIndex + 1) + ":
" + ca.command
+ " < " + response + "\n");
m_senderErrorBox->setText(tr("Error message(s)
received:\n") + errors);
if (!holding) {
holding = true; // Hold transmit while
messagebox is visible
response.clear();
m_serialPort.write("!");
m_senderErrorBox->checkBox()->setChecked(false);
qApp->beep();
int result = m_senderErrorBox->exec();
holding = false;
errors.clear();
if (m_senderErrorBox->checkBox()->isChecked())
m_settings->setIgnoreErrors(true);
if (result == QMessageBox::Ignore) m_serialPort.
write("~"); else on_cmdFileAbort_clicked();
}
}
// Check transfer complete (last row always blank, last
command row = rowcount - 2)
if (m_fileProcessedCommandIndex == m_currentModel->
rowCount() - 2
|| ca.command.contains(QRegExp("M0*2|M30")))
m_transferCompleted = true;
// Send next program commands
else if (!m_fileEndSent && (m_fileCommandIndex <
m_currentModel->rowCount()) && !holding) sendNextFileCommands();
}
// Scroll to first line on "M30" command
if (ca.command.contains("M30")) ui->tblProgram->
setCurrentIndex(m_currentModel->index(0, 1));
// Toolpath shadowing on check mode
if (m_statusCaptions.indexOf(ui->txtStatus->text()) ==
CHECK) {
GcodeViewParse *parser = m_currentDrawer->viewParser();
QList<LineSegment*> list = parser->getLineSegmentList();
if (!m_transferCompleted && m_fileProcessedCommandIndex
< m_currentModel->rowCount() - 1) {
int i;
QList<int> drawnLines;
for (i = m_lastDrawnLineIndex; i < list.count()
&& list.at(i)->getLineNumber()
<= (m_currentModel->data(m_currentModel->
index(m_fileProcessedCommandIndex, 4)).toInt()); i++) {
drawnLines << i;
}
if (!drawnLines.isEmpty() && (i < list.count())) {
m_lastDrawnLineIndex = i;
QVector3D vec = list.at(i)->getEnd();
m_toolDrawer.setToolPosition(vec);
}
foreach (int i, drawnLines) {
list.at(i)->setDrawn(true);
}
if (!drawnLines.isEmpty()) m_currentDrawer->update
(drawnLines);
} else {
foreach (LineSegment* s, list) {
if (!qIsNaN(s->getEnd().length())) {
m_toolDrawer.setToolPosition(s->getEnd());
break;
}
}
}
}
response.clear();
} else {
response.append(data + "; ");
}
} else {
// Unprocessed responses
qDebug() << "floating response:" << data;
// Handle hardware reset
if (dataIsReset(data)) {
qDebug() << "hardware reset";
m_processingFile = false;
m_transferCompleted = true;
m_fileCommandIndex = 0;
m_reseting = false;
m_homing = false;
m_lastGrblStatus = -1;
m_updateParserStatus = true;
m_statusReceived = true;
m_commands.clear();
m_queue.clear();
updateControlsState();
}
ui->txtConsole->appendPlainText(data);
}
} else {
// Blank response
// ui->txtConsole->appendPlainText(data);
}
}
Esto separa la parte del "parsing" de "data receiving" quedando más claro
todo.
¡Ya me vas contando!
… Message ID: ***@***.***>
|
Gracias por la ayuda. De hecho me gustaría poder hacer el fork y dejar
elegible el método de comunicación . Por el momento trabajaré de forma
hardcodeada, pero te comento que hay un proyecto de candle2 donde realiza
exactamente lo que has dicho, de separar el parseo de texto de la
comunicación y de hacer elegible el método de comunicación.
https://github.com/Schildkroet/Candle2.
Pero él decidió trabajar con un protocolo llamado Grip ethernet , que ni
idea de que sea. Así que lo dejo cerrado a ese protocolo. Yo igual me iba a
basar luego con su código para modificar el que tengo y dejar elegible,
separado, y todo eso para que no sea obscuro el código de candle. Lo malo
que candle2 me ha dado algunos bug en modo serial y no quise estar
corrigiendo esos bug, mientras que el otro es más estáble.
El otro que hay es iosender https://github.com/terjeio/iosender, pero me ha
dado muchos problemas, y no es fluido. Este maneja websocket y telnet. En
fin tengo muchos planes ahora que funciona con telnet el candle. Al igual
que tu no soy un experto programador y prácticamente soy nuevo en Qt. Me
gusta mucho y por eso seguiré aprendiendo.
Ya con fluid cnc funciona (toca probar muchas cosas) y debo probarlo con
grblhall. Así ya habrá un sender confiable para estos firmware.
Si gustas y tiene tiempo podemos ver lo del fork o las mejoras del
programa. Igual te puedo ayudar con lo que vayas hacer con el esp32.
…On Mon, Jun 3, 2024, 7:04 AM Rafael Dellà Bort ***@***.***> wrote:
Me alegro ;)
Si quieres ayuda para hacer un "fork" del proyecto original y añadir la
posibilidad de comunicación a través de Telnet que sea elegible desde la
GUI del programa, en vez de las modificaciones que has hecho
"hardcodeadas", dímelo. Aunque ando un poco oxidado en Qt.... :P
Da la casualidad que tengo un par de proyectos con ESP32 XD. Pero bueno,
no
me vale la pena meterme en ello. Y menos sabiendo que ahora te funciona.
Por cierto, aunque te haya funcionado hacer el "split" de los datos que
han
llegado, deberías buscar por qué no va el último código que te pase.
Si por el motivo que sea recibes media línea, tendrás dos medias líneas,
en
dos invocaciones del slot y, por lo tanto, datos corruptos.
Los paquetes de datos por la red son de 1500 bytes. Quitando la cabecera
IP
y el protocolo Telnet, igual te quedan 1400 bytes "usables" (me lo
invento).
En esos 1400 bytes el CNC tiene de sobra para enviar las respuestas. Es
más. Creo que cada vez que se invoca el slot de newData, tendrá una sola
línea, ya que, por muy rápida que sea la máquina de control numérico, no
lo
será tanto como para hacer tantas acciones que sobrepasen la capacidad de
los paquetes Ethernet.
Aun así, llámalo purismo de programador, es mejor no asumir nada y dejarlo
todo atado.
He estado mirando el código de Candle. Justamente donde está el
slot onSerialPortReadyRead es un código muy obscuro y poco mantenible:
mezcla la parte de comunicación con la parte de análisis del texto que
llega por el puerto serie. Esto hace que añadir la funcionalidad de Telnet
no sea limpio. Por eso has tenido que modificar los parámetros que pasas a
las funciones para adaptarlas a Telnet. Lo ideal sería, como te comenté,
mantener las dos opciones de comunicación; que el usuario decida cómo
conectarse. Prueba esto:
void frmMain::onSerialPortReadyRead()
{
while( m_serialPort.canReadLine() )
parseReceivedLine(m_serialPort.readLine().trimmed());
}
#define USE_SPLIT_TEXT
void frmMain::onTelnetPortReadyRead(const char *data, int count)
{
#ifdef USE_SPLIT_TEXT
for( const QByteArray &linea : QByteArray(data, count).split('\n') )
parseReceivedLine(linea.toStdString()).trimmed();
#else
static QByteArray bytesLinea;
for( int i = 0; i < count; ++i )
{
if( ((msg[i] == '\n') || (msg[i] == '\r')) && !bytesLinea.isEmpty()
)
{
parseReceivedLine(linea.toStdString.trimmed());
bytesLinea.clear();
}
}
#endif
}
Añade la función parseReceivedLine(QString data) en la definición de la
clase frmMain y modifica la actual onSerialPortReadyRead quedando así:
void frmMain::parseReceivedLine(QString data)
{
// Filter prereset responses
if (m_reseting) {
qDebug() << "reseting filter:" << data;
if (!dataIsReset(data)) continue;
else {
m_reseting = false;
m_timerStateQuery.setInterval(m_settings->queryStateTime());
}
}
// Status response
if (data[0] == '<') {
int status = -1;
m_statusReceived = true;
// Update machine coordinates
static QRegExp mpx("MPos:([^,]*),([^,]*),([^,^>^|]*)");
if (mpx.indexIn(data) != -1) {
ui->txtMPosX->setText(mpx.cap(1));
ui->txtMPosY->setText(mpx.cap(2));
ui->txtMPosZ->setText(mpx.cap(3));
}
// Status
static QRegExp stx("<([^,^>^|]*)");
if (stx.indexIn(data) != -1) {
status = m_status.indexOf(stx.cap(1));
// Undetermined status
if (status == -1) status = 0;
// Update status
if (status != m_lastGrblStatus) {
ui->txtStatus->setText(m_statusCaptions[status]);
ui->txtStatus->setStyleSheet(QString("background-color: %1;
color: %2;")
.arg(m_statusBackColors
[status]).arg(m_statusForeColors[status]));
}
// Update controls
ui->cmdRestoreOrigin->setEnabled(status == IDLE);
ui->cmdSafePosition->setEnabled(status == IDLE);
ui->cmdZeroXY->setEnabled(status == IDLE);
ui->cmdZeroZ->setEnabled(status == IDLE);
ui->chkTestMode->setEnabled(status != RUN && !m_processingFile);
ui->chkTestMode->setChecked(status == CHECK);
ui->cmdFilePause->setChecked(status == HOLD0 || status == HOLD1
|| status == QUEUE);
ui->cmdSpindle->setEnabled(!m_processingFile || status ==
HOLD0);
#ifdef WINDOWS
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
if (m_taskBarProgress) m_taskBarProgress->setPaused(status
== HOLD0 || status == HOLD1 || status == QUEUE);
}
#endif
// Update "elapsed time" timer
if (m_processingFile) {
QTime time(0, 0, 0);
int elapsed = m_startTime.elapsed();
ui->glwVisualizer->setSpendTime(time.addMSecs(elapsed));
}
// Test for job complete
if (m_processingFile && m_transferCompleted &&
((status == IDLE && m_lastGrblStatus == RUN) || status
== CHECK)) {
qDebug() << "job completed:" << m_fileCommandIndex <<
m_currentModel->rowCount() - 1;
// Shadow last segment
GcodeViewParse *parser = m_currentDrawer->viewParser();
QList<LineSegment*> list = parser->getLineSegmentList();
if (m_lastDrawnLineIndex < list.count()) {
list[m_lastDrawnLineIndex]->setDrawn(true);
m_currentDrawer->update(QList<int>() <<
m_lastDrawnLineIndex);
}
// Update state
m_processingFile = false;
m_fileProcessedCommandIndex = 0;
m_lastDrawnLineIndex = 0;
m_storedParserStatus.clear();
updateControlsState();
qApp->beep();
m_timerStateQuery.stop();
m_timerConnection.stop();
QMessageBox::information(this, qApp->applicationDisplayName(),
tr("Job done.\nTime elapsed: %1")
.arg(ui->glwVisualizer->
spendTime().toString("hh:mm:ss")));
m_timerStateQuery.setInterval(m_settings->queryStateTime());
m_timerConnection.start();
m_timerStateQuery.start();
}
// Store status
if (status != m_lastGrblStatus) m_lastGrblStatus = status;
// Abort
static double x = sNan;
static double y = sNan;
static double z = sNan;
if (m_aborting) {
switch (status) {
case IDLE: // Idle
if (!m_processingFile && m_resetCompleted) {
m_aborting = false;
restoreOffsets();
restoreParserState();
return;
}
break;
case HOLD0: // Hold
case HOLD1:
case QUEUE:
if (!m_reseting && compareCoordinates(x, y, z)) {
x = sNan;
y = sNan;
z = sNan;
grblReset();
} else {
x = ui->txtMPosX->text().toDouble();
y = ui->txtMPosY->text().toDouble();
z = ui->txtMPosZ->text().toDouble();
}
break;
}
}
}
// Store work offset
static QVector3D workOffset;
static QRegExp wpx("WCO:([^,]*),([^,]*),([^,^>^|]*)");
if (wpx.indexIn(data) != -1)
{
workOffset = QVector3D(wpx.cap(1).toDouble(), wpx.cap(2).
toDouble(), wpx.cap(3).toDouble());
}
// Update work coordinates
int prec = m_settings->units() == 0 ? 3 : 4;
ui->txtWPosX->setText(QString::number(ui->txtMPosX->text().toDouble()
- workOffset.x(), 'f', prec));
ui->txtWPosY->setText(QString::number(ui->txtMPosY->text().toDouble()
- workOffset.y(), 'f', prec));
ui->txtWPosZ->setText(QString::number(ui->txtMPosZ->text().toDouble()
- workOffset.z(), 'f', prec));
// Update tool position
QVector3D toolPosition;
if (!(status == CHECK && m_fileProcessedCommandIndex <
m_currentModel->rowCount() - 1)) {
toolPosition = QVector3D(toMetric(ui->txtWPosX->text().toDouble
()),
toMetric(ui->txtWPosY->text().
toDouble()),
toMetric(ui->txtWPosZ->text().
toDouble()));
m_toolDrawer.setToolPosition(m_codeDrawer->getIgnoreZ() ?
QVector3D(toolPosition.x(), toolPosition.y(), 0) : toolPosition);
}
// toolpath shadowing
if (m_processingFile && status != CHECK) {
GcodeViewParse *parser = m_currentDrawer->viewParser();
bool toolOntoolpath = false;
QList<int> drawnLines;
QList<LineSegment*> list = parser->getLineSegmentList();
for (int i = m_lastDrawnLineIndex; i < list.count()
&& list.at(i)->getLineNumber()
<=
(m_currentModel->data(m_currentModel->index(m_fileProcessedCommandIndex,
4)).toInt() + 1); i++) {
if (list.at(i)->contains(toolPosition)) {
toolOntoolpath = true;
m_lastDrawnLineIndex = i;
break;
}
drawnLines << i;
}
if (toolOntoolpath) {
foreach (int i, drawnLines) {
list.at(i)->setDrawn(true);
}
if (!drawnLines.isEmpty()) m_currentDrawer->update
(drawnLines);
} else if (m_lastDrawnLineIndex < list.count()) {
qDebug() << "tool missed:" << list.at
(m_lastDrawnLineIndex)->getLineNumber()
<<
m_currentModel->data(m_currentModel->index(m_fileProcessedCommandIndex,
4)).toInt()
<< m_fileProcessedCommandIndex;
}
}
// Get overridings
static QRegExp ov("Ov:([^,]*),([^,]*),([^,^>^|]*)");
if (ov.indexIn(data) != -1)
{
updateOverride(ui->slbFeedOverride, ov.cap(1).toInt(), 0x91);
updateOverride(ui->slbSpindleOverride, ov.cap(3).toInt(), 0x9a);
int rapid = ov.cap(2).toInt();
ui->slbRapidOverride->setCurrentValue(rapid);
int target = ui->slbRapidOverride->isChecked() ? ui->
slbRapidOverride->value() : 100;
if (rapid != target) switch (target) {
case 25:
m_serialPort.write(QByteArray(1, char(0x97)));
break;
case 50:
m_serialPort.write(QByteArray(1, char(0x96)));
break;
case 100:
m_serialPort.write(QByteArray(1, char(0x95)));
break;
}
// Update pins state
QString pinState;
static QRegExp pn("Pn:([^|^>]*)");
if (pn.indexIn(data) != -1) {
pinState.append(QString(tr("PS: %1")).arg(pn.cap(1)));
}
// Process spindle state
static QRegExp as("A:([^,^>^|]+)");
if (as.indexIn(data) != -1) {
QString state = as.cap(1);
m_spindleCW = state.contains("S");
if (state.contains("S") || state.contains("C")) {
m_timerToolAnimation.start(25, this);
ui->cmdSpindle->setChecked(true);
} else {
m_timerToolAnimation.stop();
ui->cmdSpindle->setChecked(false);
}
if (!pinState.isEmpty()) pinState.append(" / ");
pinState.append(QString(tr("AS: %1")).arg(as.cap(1)));
} else {
m_timerToolAnimation.stop();
ui->cmdSpindle->setChecked(false);
}
ui->glwVisualizer->setPinState(pinState);
}
// Get feed/spindle values
static QRegExp fs("FS:([^,]*),([^,^|^>]*)");
if (fs.indexIn(data) != -1) {
ui->glwVisualizer->setSpeedState((QString(tr("F/S: %1 / %2")).
arg(fs.cap(1)).arg(fs.cap(2))));
}
} else if (data.length() > 0) {
// Processed commands
if (m_commands.length() > 0 && !dataIsFloating(data)
&& !(m_commands[0].command != "[CTRL+X]" && dataIsReset(data)))
{
static QString response; // Full response string
if ((m_commands[0].command != "[CTRL+X]" && dataIsEnd(data))
|| (m_commands[0].command == "[CTRL+X]" &&
dataIsReset(data)))
{
response.append(data);
// Take command from buffer
CommandAttributes ca = m_commands.takeFirst();
QTextBlock tb = ui->txtConsole->document()->
findBlockByNumber(ca.consoleIndex);
QTextCursor tc(tb);
// Restore absolute/relative coordinate system after jog
if (ca.command.toUpper() == "$G" && ca.tableIndex == -2) {
if (ui->chkKeyboardControl->isChecked())
m_absoluteCoordinates = response.contains("G90");
else if (response.contains("G90")) sendCommand("G90", -1,
m_settings->showUICommands());
}
// Jog
if (ca.command.toUpper().contains("$J=") && ca.tableIndex ==
-2) {
jogStep();
}
// Process parser status
if (ca.command.toUpper() == "$G" && ca.tableIndex == -3) {
// Update status in visualizer window
ui->glwVisualizer->setParserStatus(response.left(
response.indexOf("; ")));
// Store parser status
if (m_processingFile) storeParserState();
// Spindle speed
QRegExp rx(".*S([\\d\\.]+)");
if (rx.indexIn(response) != -1) {
double speed = toMetric(rx.cap(1).toDouble());
//RPM in imperial?
ui->slbSpindle->setCurrentValue(speed);
}
m_updateParserStatus = true;
}
// Store origin
if (ca.command == "$#" && ca.tableIndex == -2) {
qDebug() << "Received offsets:" << response;
QRegExp rx(".*G92:([^,]*),([^,]*),([^\\]]*)");
if (rx.indexIn(response) != -1) {
if (m_settingZeroXY) {
m_settingZeroXY = false;
m_storedX = toMetric(rx.cap(1).toDouble());
m_storedY = toMetric(rx.cap(2).toDouble());
} else if (m_settingZeroZ) {
m_settingZeroZ = false;
m_storedZ = toMetric(rx.cap(3).toDouble());
}
ui->cmdRestoreOrigin->setToolTip(QString(tr("Restore
origin:\n%1, %2, %3")).arg(m_storedX).arg(m_storedY).arg(m_storedZ));
}
}
// Homing response
if ((ca.command.toUpper() == "$H" || ca.command.toUpper() ==
"$T") && m_homing) m_homing = false;
// Reset complete
if (ca.command == "[CTRL+X]") {
m_resetCompleted = true;
m_updateParserStatus = true;
}
// Clear command buffer on "M2" & "M30" command (old
firmwares)
if ((ca.command.contains("M2") || ca.command.contains("M30"))
&& response.contains("ok") && !response.contains("[Pgm End]")) {
m_commands.clear();
m_queue.clear();
}
// Process probing on heightmap mode only from table
commands
if (ca.command.contains("G38.2") && m_heightMapMode && ca.
tableIndex > -1) {
// Get probe Z coordinate
// "[PRB:0.000,0.000,0.000:0];ok"
QRegExp rx(".*PRB:([^,]*),([^,]*),([^]^:]*)");
double z = qQNaN();
if (rx.indexIn(response) != -1) {
qDebug() << "probing coordinates:" << rx.cap(1) <<
rx.cap(2) << rx.cap(3);
z = toMetric(rx.cap(3).toDouble());
}
static double firstZ;
if (m_probeIndex == -1) {
firstZ = z;
z = 0;
} else {
// Calculate delta Z
z -= firstZ;
// Calculate table indexes
int row = trunc(m_probeIndex / m_heightMapModel.
columnCount());
int column = m_probeIndex - row * m_heightMapModel.
columnCount();
if (row % 2) column = m_heightMapModel.columnCount()
- 1 - column;
// Store Z in table
m_heightMapModel.setData(m_heightMapModel.index(row,
column), z, Qt::UserRole);
ui->tblHeightMap->update(m_heightMapModel.index(
m_heightMapModel.rowCount() - 1 - row, column));
updateHeightMapInterpolationDrawer();
}
m_probeIndex++;
}
// Change state query time on check mode on
if (ca.command.contains(QRegExp("$[cC]"))) {
m_timerStateQuery.setInterval(response.contains("Enable")
? 1000 : m_settings->queryStateTime());
}
// Add response to console
if (tb.isValid() && tb.text() == ca.command) {
bool scrolledDown = ui->txtConsole->verticalScrollBar
()->value() == ui->txtConsole->verticalScrollBar()->maximum();
// Update text block numbers
int blocksAdded = response.count("; ");
if (blocksAdded > 0) for (int i = 0; i < m_commands.
count(); i++) {
if (m_commands[i].consoleIndex != -1) m_commands[i].
consoleIndex += blocksAdded;
}
tc.beginEditBlock();
tc.movePosition(QTextCursor::EndOfBlock);
tc.insertText(" < " + QString(response).replace("; ", "
\r\n"));
tc.endEditBlock();
if (scrolledDown) ui->txtConsole->verticalScrollBar()->
setValue(ui->txtConsole->verticalScrollBar()->maximum());
}
// Check queue
if (m_queue.length() > 0) {
CommandQueue cq = m_queue.takeFirst();
while ((bufferLength() + cq.command.length() + 1) <=
BUFFERLENGTH) {
sendCommand(cq.command, cq.tableIndex, cq.
showInConsole);
if (m_queue.isEmpty()) break; else cq = m_queue.
takeFirst();
}
}
// Add response to table, send next program commands
if (m_processingFile) {
// Only if command from table
if (ca.tableIndex > -1) {
m_currentModel->setData(m_currentModel->index(ca.
tableIndex, 2), GCodeItem::Processed);
m_currentModel->setData(m_currentModel->index(ca.
tableIndex, 3), response);
m_fileProcessedCommandIndex = ca.tableIndex;
if (ui->chkAutoScroll->isChecked() && ca.tableIndex
!= -1) {
ui->tblProgram->scrollTo(m_currentModel->index(
ca.tableIndex + 1, 0)); // TODO: Update by timer
ui->tblProgram->setCurrentIndex(m_currentModel->
index(ca.tableIndex, 1));
}
}
// Update taskbar progress
#ifdef WINDOWS
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7)
{
if (m_taskBarProgress) m_taskBarProgress->setValue
(m_fileProcessedCommandIndex);
}
#endif
// Process error messages
static bool holding = false;
static QString errors;
if (ca.tableIndex > -1 && response.toUpper().contains(
"ERROR") && !m_settings->ignoreErrors()) {
errors.append(QString::number(ca.tableIndex + 1) + ":
" + ca.command
+ " < " + response + "\n");
m_senderErrorBox->setText(tr("Error message(s)
received:\n") + errors);
if (!holding) {
holding = true; // Hold transmit while
messagebox is visible
response.clear();
m_serialPort.write("!");
m_senderErrorBox->checkBox()->setChecked(false);
qApp->beep();
int result = m_senderErrorBox->exec();
holding = false;
errors.clear();
if (m_senderErrorBox->checkBox()->isChecked())
m_settings->setIgnoreErrors(true);
if (result == QMessageBox::Ignore) m_serialPort.
write("~"); else on_cmdFileAbort_clicked();
}
}
// Check transfer complete (last row always blank, last
command row = rowcount - 2)
if (m_fileProcessedCommandIndex == m_currentModel->
rowCount() - 2
|| ca.command.contains(QRegExp("M0*2|M30")))
m_transferCompleted = true;
// Send next program commands
else if (!m_fileEndSent && (m_fileCommandIndex <
m_currentModel->rowCount()) && !holding) sendNextFileCommands();
}
// Scroll to first line on "M30" command
if (ca.command.contains("M30")) ui->tblProgram->
setCurrentIndex(m_currentModel->index(0, 1));
// Toolpath shadowing on check mode
if (m_statusCaptions.indexOf(ui->txtStatus->text()) ==
CHECK) {
GcodeViewParse *parser = m_currentDrawer->viewParser();
QList<LineSegment*> list = parser->getLineSegmentList();
if (!m_transferCompleted && m_fileProcessedCommandIndex
< m_currentModel->rowCount() - 1) {
int i;
QList<int> drawnLines;
for (i = m_lastDrawnLineIndex; i < list.count()
&& list.at(i)->getLineNumber()
<= (m_currentModel->data(m_currentModel->
index(m_fileProcessedCommandIndex, 4)).toInt()); i++) {
drawnLines << i;
}
if (!drawnLines.isEmpty() && (i < list.count())) {
m_lastDrawnLineIndex = i;
QVector3D vec = list.at(i)->getEnd();
m_toolDrawer.setToolPosition(vec);
}
foreach (int i, drawnLines) {
list.at(i)->setDrawn(true);
}
if (!drawnLines.isEmpty()) m_currentDrawer->update
(drawnLines);
} else {
foreach (LineSegment* s, list) {
if (!qIsNaN(s->getEnd().length())) {
m_toolDrawer.setToolPosition(s->getEnd());
break;
}
}
}
}
response.clear();
} else {
response.append(data + "; ");
}
} else {
// Unprocessed responses
qDebug() << "floating response:" << data;
// Handle hardware reset
if (dataIsReset(data)) {
qDebug() << "hardware reset";
m_processingFile = false;
m_transferCompleted = true;
m_fileCommandIndex = 0;
m_reseting = false;
m_homing = false;
m_lastGrblStatus = -1;
m_updateParserStatus = true;
m_statusReceived = true;
m_commands.clear();
m_queue.clear();
updateControlsState();
}
ui->txtConsole->appendPlainText(data);
}
} else {
// Blank response
// ui->txtConsole->appendPlainText(data);
}
}
Esto separa la parte del "parsing" de "data receiving" quedando más claro
todo.
¡Ya me vas contando!
> Message ID: ***@***.***>
>
—
Reply to this email directly, view it on GitHub
<#3 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACPACMXJGW3WJQZW4W4KX43ZFRSUTAVCNFSM6AAAAABIL7ZJJSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNBVGE2TEOJRHA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
in serial comunication is posible this while (m_serialPort.canReadLine()), is posible in this program?
The text was updated successfully, but these errors were encountered: