OSDN Git Service

Several improvements and bug fixes.
[dennco/dennco.git] / Source / platform / qt / qtdnserialportimpl.cpp
1 //  Copyright (c) 2012 Dennco Project
2 //
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 //
17 //  Created by tkawata on Apr/15/2012.
18 //
19 #include "qtdnserialportimpl.h"
20
21 #include "ui_portinfodialog.h"
22 #include "DNServerSerialPort.h"
23 #include "DNThread.h"
24 #include "mainwindow.h"
25 #include "TKLog.h"
26 #include "DNUtils.h"
27 #include "DNSettings.h"
28
29 #include <QThread>
30 #include <QtCore/QVariant>
31
32 //
33 #include "DNEngine.h"
34
35 class SleeperThread : public QThread
36 {
37 public:
38     static void msleep(unsigned long msecs)
39     {
40         QThread::msleep(msecs);
41     }
42 };
43
44 DNSerialPortImpl * DNSerialPortImpl::create(DNServerSerialPort *server)
45 {
46     return new QtDNSerialPortImpl(server);
47 }
48
49
50 QtDNSerialPortImpl::QtDNSerialPortImpl(DNServerSerialPort *server) :
51     DNSerialPortImpl(server), mSerialCommunicationThread(NULL), mHandlerThread(NULL), mStopping(false),
52     mResponseString(""),
53     mPortName(""),
54     mBaudRate(SerialPort::Rate9600),
55     mDataBits(SerialPort::Data8),
56     mParity(SerialPort::NoParity),
57     mStopBits(SerialPort::OneStop),
58     mFlowControl(SerialPort::NoFlowControl)
59 {
60 }
61
62 QtDNSerialPortImpl::~QtDNSerialPortImpl()
63 {
64     stop();
65     if (mSerialCommunicationThread)
66     {
67         delete mSerialCommunicationThread;
68         mSerialCommunicationThread = NULL;
69     }
70     if (mHandlerThread)
71     {
72         delete mHandlerThread;
73         mHandlerThread = NULL;
74     }
75 }
76
77 bool QtDNSerialPortImpl::setup()
78 {
79     if (!MainWindow::instance)
80         return false;
81
82     QtDNSerialPortSettingDialog dialog(MainWindow::instance);
83     dialog.exec();
84
85     if (dialog.isOK())
86     {
87         mPortName = dialog.getSelectedPortName();
88         mBaudRate = (SerialPort::Rate) dialog.getSelectedRate();
89         mDataBits = dialog.getSelectedDataBits();
90         mParity = dialog.getSelectedParity();
91         mStopBits = dialog.getSelectedStopBits();
92         mFlowControl = dialog.getSelectedFlowControl();
93
94         return true;
95     }
96     return false;
97
98 }
99
100 bool QtDNSerialPortImpl::isRunning()
101 {
102     DNLocker lock(&mStartStopLock);
103     return !mStopping;
104 }
105
106 void QtDNSerialPortImpl::start()
107 {
108     stop();
109     mStartStopLock.lock();
110     if (mStopping)
111     {
112         mStopping = false;
113         mSerialCommunicationThread = DNThread::createThread(QtDNSerialPortImpl::serialCommunicationThreadBody, this);
114         mHandlerThread = DNThread::createThread(QtDNSerialPortImpl::requestHandelrThreadBody, this);
115
116         mSerialCommunicationThread->start();
117         mHandlerThread->start();
118     }
119     mStartStopLock.unlock();
120 }
121
122 void QtDNSerialPortImpl::stop()
123 {
124     mStartStopLock.lock();
125     if (!mStopping)
126     {
127         mStopping = true;
128         if (mSerialCommunicationThread)
129         {
130             mSerialCommunicationThread->waitForExit(1000);
131             delete mSerialCommunicationThread;
132             mSerialCommunicationThread = NULL;
133         }
134         if (mHandlerThread)
135         {
136             mHandlerThread->waitForExit(1000);
137             delete mHandlerThread;
138             mHandlerThread = NULL;
139         }
140         mRequestQueue.clear();
141     }
142     mStartStopLock.unlock();
143 }
144
145 void QtDNSerialPortImpl::sendMessage(const char* message)
146 {
147     mResponseStringLock.lock();
148     mResponseString.append(message);
149     mResponseStringLock.unlock();
150     mSerialWriteSemaphore.release(1);
151 }
152
153 //static
154 void QtDNSerialPortImpl::serialCommunicationThreadBody(void *_impl)
155 {
156     if (!_impl)
157         return;
158
159     QtDNSerialPortImpl *impl = (QtDNSerialPortImpl*)_impl;
160
161     if (impl->mPortName.length()==0 || impl->mBaudRate == 0)
162     {
163         impl->mStartStopLock.lock();
164         impl->mStopping = true;
165         impl->mStartStopLock.unlock();
166         return;
167     }
168
169     SerialPort *port = NULL;
170     bool r = true;
171     int retry = 6;
172     do
173     {
174         port = new SerialPort(impl->mPortName);
175         r = port != NULL;
176
177         if (r)
178         {
179             r = port->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
180         }
181         if (r)
182         {
183             if (!port->setRate(impl->mBaudRate))
184             {
185                 r = false;
186             }
187         }
188         if (r)
189         {
190             if (!port->setDataBits(impl->mDataBits))
191             {
192                 r = false;
193             }
194         }
195         if (r)
196         {
197             if (!port->setParity(impl->mParity))
198             {
199                 r = false;
200             }
201         }
202         if (r)
203         {
204             if (!port->setStopBits(impl->mStopBits))
205             {
206                 r = false;
207             }
208         }
209         if (r)
210         {
211             if (!port->setFlowControl(impl->mFlowControl))
212             {
213                 r = false;
214             }
215         }
216
217         if (!r)
218         {
219             SleeperThread::msleep(500);
220             if (port)
221             {
222                 port->close();
223                 delete port;
224                 port = NULL;
225             }
226             retry--;
227             TKLog::printf("Failed to open the serial port. retrying...");
228         }
229     } while (!r && retry >= 0);
230
231
232     if (!r)
233     {
234         if (port)
235             delete port;
236
237         impl->mStartStopLock.lock();
238         impl->mStopping = true;
239         impl->mStartStopLock.unlock();
240         TKLog::printf("Failed to open the serial port. giving up!");
241         return;
242     }
243
244     int waitTime = 1;
245
246     char buffer[1024];
247     int p = 0;
248     while(!impl->mStopping)
249     {
250         if (port->bytesAvailable() > 0)
251         {
252             int len = port->read( (buffer + p), sizeof(buffer) - p);
253             if (len != -1)
254             {
255                 int i = p;
256                 while (i < p + len)
257                 {
258                     if (buffer[i] == '\r' || buffer[i] == '\n' || i == sizeof(buffer)-1)
259                     {
260                         if (i>0)
261                         {
262                             buffer[i] = 0;
263                             impl->mRequestQueueLock.lock();
264                             impl->mRequestQueue.push_back(QString::fromLocal8Bit(buffer));
265                             impl->mRequestQueueLock.unlock();
266                             impl->mQueueSemaphore.release(1);
267                         }
268                         int j = i + 1;
269                         while(j < p + len)
270                         {
271                             if (buffer[j]!='\r' && buffer[j]!='\n')
272                             {
273                                 break;
274                             }
275                             j++;
276                         }
277                         int d = 0;
278                         while (j < p + len)
279                         {
280                             buffer[d] = buffer[j];
281                             d++;
282                             j++;
283                         }
284                         p = 0;
285                         i = 0;
286                         len = d;
287                         continue;
288                     }
289                     i++;
290                 }
291                 p = p + len;
292                 waitTime = 1;
293             }
294         }
295         else
296         {
297             if (!impl->mSerialWriteSemaphore.tryAcquire(1,waitTime))
298             {
299                 waitTime++;
300                 if (waitTime>100)
301                 {
302                     waitTime = 100;
303                     if (!port->isOpen())
304                     {
305                         //TODO
306                     }
307                 }
308             }
309         }
310
311         impl->mResponseStringLock.lock();
312         if (impl->mResponseString.length()>0 && port->isOpen())
313         {
314             QString response = impl->mResponseString;
315             impl->mResponseString.clear();
316             impl->mResponseStringLock.unlock();
317             port->write(response.toLocal8Bit());
318         }
319         else
320         {
321             impl->mResponseStringLock.unlock();
322         }
323
324     }
325     port->close();
326     delete port;
327 }
328
329 //static
330 void QtDNSerialPortImpl::requestHandelrThreadBody(void *_impl)
331 {
332     if (!_impl)
333         return;
334
335     QtDNSerialPortImpl *impl = (QtDNSerialPortImpl*)_impl;
336     while(!impl->mStopping)
337     {
338         impl->mQueueSemaphore.tryAcquire(1,100);
339         impl->mRequestQueueLock.lock();
340         if (!impl->mRequestQueue.isEmpty())
341         {
342             QString line = impl->mRequestQueue.dequeue();
343             impl->mRequestQueueLock.unlock();
344
345             if (line.length()>3)
346             {
347                 QString seq = line.left(2);
348                 QString cmd = line.mid(2,1);
349                 QString par = line.mid(3);
350                 if (cmd == "M")
351                 {
352                     //message
353                     TKLog::printf("(SERIALl):%s", par.toLocal8Bit().constData());
354                 }
355                 else if (cmd == "S")
356                 {
357                     //set data
358                     int idx = par.indexOf(",");
359                     if (idx > 0 && par.length() > idx + 1)
360                     {
361                         QString path = par.left(idx);
362                         float val = par.mid(idx+1).toFloat();
363                         impl->mServer->getEngine()->doClientSetRequest(path.toLocal8Bit().constData(),val);
364                     }
365                 }
366                 else if (cmd == "G")
367                 {
368                     //get data
369                     float val = impl->mServer->getEngine()->doClientGetRequest(par.toLocal8Bit().constData());
370                     QString rep = seq;
371                     rep.append(QString::number(val));
372                     rep.append("\n");
373                     impl->sendMessage(rep.toLocal8Bit().constData());
374                 }
375             }
376         }
377         else
378         {
379             impl->mRequestQueueLock.unlock();
380         }
381
382     }
383 }
384
385
386 Q_DECLARE_METATYPE(SerialPortInfo)
387
388 QtDNSerialPortSettingDialog::QtDNSerialPortSettingDialog(QWidget *parent)
389     : QDialog(parent)
390     , ui(new Ui::QtDNSerialPortSettingDialog)
391     , mIsOK(false)
392 {
393     ui->setupUi(this);
394     procUpdateAvailablePorts();
395     procItemPortChanged(0);
396
397     connect(ui->updateButton, SIGNAL(clicked()), this, SLOT(procUpdateAvailablePorts()));
398     connect(ui->portsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(procItemPortChanged(int)));
399     connect(ui->okPushButton, SIGNAL(clicked()), this, SLOT(procOKButtonClick()));
400     connect(ui->cancelPushButton, SIGNAL(clicked()), this, SLOT(procCancelButtonClick()));
401
402     //default setting
403     QString defaultPortName = QString::fromStdString(DNSettings::getValue(DNSettings::SERIAL_PORTNAME , ""));
404     for (int i = 0; i < ui->portsComboBox->count(); i++)
405     {
406         if (ui->portsComboBox->itemText(i) == defaultPortName)
407         {
408             ui->portsComboBox->setCurrentIndex(i);
409             break;
410         }
411     }
412
413     QString defaultBaudRate = QString::fromStdString(DNSettings::getValue(DNSettings::SERIAL_RATE, "9600"));
414     for (int i = 0; i < ui->ratesComboBox->count(); i++)
415     {
416         if (ui->ratesComboBox->itemText(i) == defaultBaudRate)
417         {
418             ui->ratesComboBox->setCurrentIndex(i);
419             break;
420         }
421     }
422
423     QString defaultDataBits = QString::fromStdString(DNSettings::getValue(DNSettings::SERIAL_DATABITS, "8"));
424     for (int i = 0; i < ui->dataBitsComboBox->count(); i++)
425     {
426         if (ui->dataBitsComboBox->itemText(i) == defaultDataBits)
427         {
428             ui->dataBitsComboBox->setCurrentIndex(i);
429             break;
430         }
431     }
432
433     QString defaultParity = QString::fromStdString(DNSettings::getValue(DNSettings::SERIAL_PARITY, "No parity"));
434     for (int i = 0; i < ui->parityComboBox->count(); i++)
435     {
436         if (ui->parityComboBox->itemText(i) == defaultParity)
437         {
438             ui->parityComboBox->setCurrentIndex(i);
439             break;
440         }
441     }
442
443     QString defaultStopBits = QString::fromStdString(DNSettings::getValue(DNSettings::SERIAL_STOPBITS, "one stop"));
444     for (int i = 0; i < ui->stopBitsComboBox->count(); i++)
445     {
446         if (ui->stopBitsComboBox->itemText(i) == defaultStopBits)
447         {
448             ui->stopBitsComboBox->setCurrentIndex(i);
449             break;
450         }
451     }
452
453     QString defaultFlowControl = QString::fromStdString(DNSettings::getValue(DNSettings::SERIAL_FLOWCONTROL, "No flow control"));
454     for (int i = 0; i < ui->flowControlComboBox->count(); i++)
455     {
456         if (ui->flowControlComboBox->itemText(i) == defaultFlowControl)
457         {
458             ui->flowControlComboBox->setCurrentIndex(i);
459             break;
460         }
461     }
462
463 }
464
465 QtDNSerialPortSettingDialog::~QtDNSerialPortSettingDialog()
466 {
467     delete ui;
468 }
469
470 QString QtDNSerialPortSettingDialog::getSelectedPortName()
471 {
472     return ui->portsComboBox->currentText();
473 }
474
475 int QtDNSerialPortSettingDialog::getSelectedRate()
476 {
477     return ui->ratesComboBox->currentText().toInt();
478 }
479
480 SerialPort::DataBits QtDNSerialPortSettingDialog::getSelectedDataBits()
481 {
482     SerialPort::DataBits r = SerialPort::UnknownDataBits;
483
484     switch(ui->dataBitsComboBox->currentIndex())
485     {
486     case 0:
487         r = SerialPort::Data5;
488         break;
489     case 1:
490         r = SerialPort::Data6;
491         break;
492     case 2:
493         r = SerialPort::Data7;
494         break;
495     case 3:
496         r = SerialPort::Data8;
497         break;
498     }
499
500     return r;
501 }
502
503 SerialPort::Parity   QtDNSerialPortSettingDialog::getSelectedParity()
504 {
505     SerialPort::Parity r = SerialPort::UnknownParity;
506
507     switch(ui->parityComboBox->currentIndex())
508     {
509     case 0:
510         r = SerialPort::NoParity;
511         break;
512     case 1:
513         r = SerialPort::EvenParity;
514         break;
515     case 2:
516         r = SerialPort::OddParity;
517         break;
518     case 3:
519         r = SerialPort::SpaceParity;
520         break;
521     case 4:
522         r = SerialPort::MarkParity;
523         break;
524     }
525
526     return r;
527 }
528
529 SerialPort::StopBits QtDNSerialPortSettingDialog::getSelectedStopBits()
530 {
531     SerialPort::StopBits r = SerialPort::UnknownStopBits;
532
533     switch(ui->stopBitsComboBox->currentIndex())
534     {
535     case 0:
536         r = SerialPort::OneStop;
537         break;
538     case 1:
539         r = SerialPort::OneAndHalfStop;
540         break;
541     case 2:
542         r = SerialPort::TwoStop;
543         break;
544     }
545
546     return r;
547 }
548
549 SerialPort::FlowControl QtDNSerialPortSettingDialog::getSelectedFlowControl()
550 {
551     SerialPort::FlowControl r = SerialPort::UnknownFlowControl;
552
553     switch(ui->flowControlComboBox->currentIndex())
554     {
555     case 0:
556         r = SerialPort::NoFlowControl;
557         break;
558     case 1:
559         r = SerialPort::HardwareControl;
560         break;
561     case 2:
562         r = SerialPort::SoftwareControl;
563         break;
564     }
565
566     return r;
567 }
568
569
570 void QtDNSerialPortSettingDialog::procUpdateAvailablePorts()
571 {
572     ui->portsComboBox->clear();
573     foreach (SerialPortInfo info, SerialPortInfo::availablePorts()) {
574         QVariant v;
575         v.setValue(info);
576         ui->portsComboBox->addItem(info.portName(), v);
577     }
578
579     ui->okPushButton->setEnabled(ui->portsComboBox->count() > 0);
580 }
581
582 void QtDNSerialPortSettingDialog::procItemPortChanged(int idx)
583 {
584     QVariant v = ui->portsComboBox->itemData(idx);
585     if (v.isValid()) {
586         SerialPortInfo info = v.value<SerialPortInfo>();
587
588         ui->locationValueLabel->setText(info.systemLocation());
589         ui->descriptionValueLabel->setText(info.description());
590         ui->manufacturerValueLabel->setText(info.manufacturer());
591     }
592 }
593
594 void QtDNSerialPortSettingDialog::procOKButtonClick()
595 {
596     mIsOK = true;
597     done(1);
598 }
599
600 void QtDNSerialPortSettingDialog::procCancelButtonClick()
601 {
602     mIsOK = false;
603     done(0);
604 }
605
606 bool QtDNSerialPortSettingDialog::isOK()
607 {
608     // save selections
609     DNSettings::setValue(DNSettings::SERIAL_PORTNAME, ui->portsComboBox->currentText().toStdString());
610     DNSettings::setValue(DNSettings::SERIAL_RATE, ui->ratesComboBox->currentText().toStdString());
611     DNSettings::setValue(DNSettings::SERIAL_DATABITS, ui->dataBitsComboBox->currentText().toStdString());
612     DNSettings::setValue(DNSettings::SERIAL_PARITY, ui->parityComboBox->currentText().toStdString());
613     DNSettings::setValue(DNSettings::SERIAL_STOPBITS, ui->stopBitsComboBox->currentText().toStdString());
614     DNSettings::setValue(DNSettings::SERIAL_FLOWCONTROL, ui->flowControlComboBox->currentText().toStdString());
615
616     return mIsOK;
617 }