You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
449 lines
16 KiB
449 lines
16 KiB
#include <QDebug> |
|
|
|
#include <XbeeConfigurationWindow.h> |
|
#include <XbeeLinkInterface.h> |
|
#include <QDir> |
|
#include <QSettings> |
|
#include <QFileInfoList> |
|
#include <qdatastream.h> |
|
|
|
#ifdef _WIN32 |
|
#include <QextSerialEnumerator.h> |
|
#endif |
|
|
|
#if defined (__APPLE__) && defined (__MACH__) |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <sys/ioctl.h> |
|
#include <errno.h> |
|
#include <paths.h> |
|
#include <termios.h> |
|
#include <sysexits.h> |
|
#include <sys/param.h> |
|
#include <sys/select.h> |
|
#include <sys/time.h> |
|
#include <time.h> |
|
#include <AvailabilityMacros.h> |
|
|
|
#ifdef __MWERKS__ |
|
#define __CF_USE_FRAMEWORK_INCLUDES__ |
|
#endif |
|
|
|
|
|
#include <CoreFoundation/CoreFoundation.h> |
|
|
|
#include <IOKit/IOKitLib.h> |
|
#include <IOKit/serial/IOSerialKeys.h> |
|
#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3) |
|
#include <IOKit/serial/ioss.h> |
|
#endif |
|
#include <IOKit/IOBSD.h> |
|
|
|
// Apple internal modems default to local echo being on. If your modem has local echo disabled, |
|
// undefine the following macro. |
|
#define LOCAL_ECHO |
|
|
|
#define kATCommandString "AT\r" |
|
|
|
#ifdef LOCAL_ECHO |
|
#define kOKResponseString "AT\r\r\nOK\r\n" |
|
#else |
|
#define kOKResponseString "\r\nOK\r\n" |
|
#endif |
|
#endif |
|
|
|
|
|
// Some helper functions for serial port enumeration |
|
#if defined (__APPLE__) && defined (__MACH__) |
|
|
|
enum { |
|
kNumRetries = 3 |
|
}; |
|
|
|
// Function prototypes |
|
static kern_return_t FindModems(io_iterator_t *matchingServices); |
|
static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize); |
|
|
|
// Returns an iterator across all known modems. Caller is responsible for |
|
// releasing the iterator when iteration is complete. |
|
static kern_return_t FindModems(io_iterator_t *matchingServices) |
|
{ |
|
kern_return_t kernResult; |
|
CFMutableDictionaryRef classesToMatch; |
|
|
|
/*! @function IOServiceMatching |
|
@abstract Create a matching dictionary that specifies an IOService class match. |
|
@discussion A very common matching criteria for IOService is based on its class. IOServiceMatching will create a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by C-string name. |
|
@param name The class name, as a const C-string. Class matching is successful on IOService's of this class or any subclass. |
|
@result The matching dictionary created, is returned on success, or zero on failure. The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification which will consume a reference, otherwise it should be released with CFRelease by the caller. */ |
|
|
|
// Serial devices are instances of class IOSerialBSDClient |
|
classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); |
|
if (classesToMatch == NULL) { |
|
printf("IOServiceMatching returned a NULL dictionary.\n"); |
|
} else { |
|
/*! |
|
@function CFDictionarySetValue |
|
Sets the value of the key in the dictionary. |
|
@param theDict The dictionary to which the value is to be set. If this |
|
parameter is not a valid mutable CFDictionary, the behavior is |
|
undefined. If the dictionary is a fixed-capacity dictionary and |
|
it is full before this operation, and the key does not exist in |
|
the dictionary, the behavior is undefined. |
|
@param key The key of the value to set into the dictionary. If a key |
|
which matches this key is already present in the dictionary, only |
|
the value is changed ("add if absent, replace if present"). If |
|
no key matches the given key, the key-value pair is added to the |
|
dictionary. If added, the key is retained by the dictionary, |
|
using the retain callback provided |
|
when the dictionary was created. If the key is not of the sort |
|
expected by the key retain callback, the behavior is undefined. |
|
@param value The value to add to or replace into the dictionary. The value |
|
is retained by the dictionary using the retain callback provided |
|
when the dictionary was created, and the previous value if any is |
|
released. If the value is not of the sort expected by the |
|
retain or release callbacks, the behavior is undefined. |
|
*/ |
|
CFDictionarySetValue(classesToMatch, |
|
CFSTR(kIOSerialBSDTypeKey), |
|
CFSTR(kIOSerialBSDModemType)); |
|
|
|
// Each serial device object has a property with key |
|
// kIOSerialBSDTypeKey and a value that is one of kIOSerialBSDAllTypes, |
|
// kIOSerialBSDModemType, or kIOSerialBSDRS232Type. You can experiment with the |
|
// matching by changing the last parameter in the above call to CFDictionarySetValue. |
|
|
|
// As shipped, this sample is only interested in modems, |
|
// so add this property to the CFDictionary we're matching on. |
|
// This will find devices that advertise themselves as modems, |
|
// such as built-in and USB modems. However, this match won't find serial modems. |
|
} |
|
|
|
/*! @function IOServiceGetMatchingServices |
|
@abstract Look up registered IOService objects that match a matching dictionary. |
|
@discussion This is the preferred method of finding IOService objects currently registered by IOKit. IOServiceAddNotification can also supply this information and install a notification of new IOServices. The matching information used in the matching dictionary may vary depending on the class of service being looked up. |
|
@param masterPort The master port obtained from IOMasterPort(). |
|
@param matching A CF dictionary containing matching information, of which one reference is consumed by this function. IOKitLib can contruct matching dictionaries for common criteria with helper functions such as IOServiceMatching, IOOpenFirmwarePathMatching. |
|
@param existing An iterator handle is returned on success, and should be released by the caller when the iteration is finished. |
|
@result A kern_return_t error code. */ |
|
|
|
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); |
|
if (KERN_SUCCESS != kernResult) { |
|
printf("IOServiceGetMatchingServices returned %d\n", kernResult); |
|
goto exit; |
|
} |
|
|
|
exit: |
|
return kernResult; |
|
} |
|
|
|
/** Given an iterator across a set of modems, return the BSD path to the first one. |
|
* If no modems are found the path name is set to an empty string. |
|
*/ |
|
static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize) |
|
{ |
|
io_object_t modemService; |
|
kern_return_t kernResult = KERN_FAILURE; |
|
Boolean modemFound = false; |
|
|
|
// Initialize the returned path |
|
*bsdPath = '\0'; |
|
|
|
// Iterate across all modems found. In this example, we bail after finding the first modem. |
|
|
|
while ((modemService = IOIteratorNext(serialPortIterator)) && !modemFound) { |
|
CFTypeRef bsdPathAsCFString; |
|
|
|
// Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be |
|
// used: the dialin device (/dev/tty.xxxxx) would be used when monitoring a serial port for |
|
// incoming calls, e.g. a fax listener. |
|
|
|
bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService, |
|
CFSTR(kIOCalloutDeviceKey), |
|
kCFAllocatorDefault, |
|
0); |
|
if (bsdPathAsCFString) { |
|
Boolean result; |
|
|
|
// Convert the path from a CFString to a C (NUL-terminated) string for use |
|
// with the POSIX open() call. |
|
|
|
result = CFStringGetCString((CFStringRef)bsdPathAsCFString, |
|
bsdPath, |
|
maxPathSize, |
|
kCFStringEncodingUTF8); |
|
CFRelease(bsdPathAsCFString); |
|
|
|
if (result) { |
|
//printf("Modem found with BSD path: %s", bsdPath); |
|
modemFound = true; |
|
kernResult = KERN_SUCCESS; |
|
} |
|
} |
|
|
|
printf("\n"); |
|
|
|
// Release the io_service_t now that we are done with it. |
|
|
|
(void) IOObjectRelease(modemService); |
|
} |
|
|
|
return kernResult; |
|
} |
|
#endif |
|
|
|
XbeeConfigurationWindow::XbeeConfigurationWindow(LinkInterface* link, QWidget *parent, Qt::WindowFlags flags): QWidget(parent, flags), |
|
userConfigured(false) |
|
{ |
|
XbeeLinkInterface *xbeeLink = dynamic_cast<XbeeLinkInterface*>(link); |
|
|
|
if(xbeeLink != 0) |
|
{ |
|
this->link = xbeeLink; |
|
|
|
action = new QAction(QIcon(":/images/devices/network-wireless.svg"), "", link); |
|
|
|
baudLabel = new QLabel; |
|
baudLabel->setText(tr("Baut Rate")); |
|
baudBox = new QComboBox; |
|
baudLabel->setBuddy(baudBox); |
|
portLabel = new QLabel; |
|
portLabel->setText(tr("SerialPort")); |
|
portBox = new QComboBox; |
|
portBox->setEditable(true); |
|
portLabel->setBuddy(portBox); |
|
highAddrLabel = new QLabel; |
|
highAddrLabel->setText(tr("Remote hex Address &High")); |
|
highAddr = new HexSpinBox(this); |
|
highAddrLabel->setBuddy(highAddr); |
|
lowAddrLabel = new QLabel; |
|
lowAddrLabel->setText(tr("Remote hex Address &Low")); |
|
lowAddr = new HexSpinBox(this); |
|
lowAddrLabel->setBuddy(lowAddr); |
|
actionLayout = new QGridLayout; |
|
actionLayout->addWidget(baudLabel,1,1); |
|
actionLayout->addWidget(baudBox,1,2); |
|
actionLayout->addWidget(portLabel,2,1); |
|
actionLayout->addWidget(portBox,2,2); |
|
actionLayout->addWidget(highAddrLabel,3,1); |
|
actionLayout->addWidget(highAddr,3,2); |
|
actionLayout->addWidget(lowAddrLabel,4,1); |
|
actionLayout->addWidget(lowAddr,4,2); |
|
tmpLayout = new QVBoxLayout; |
|
tmpLayout->addStretch(); |
|
tmpLayout->addLayout(actionLayout); |
|
xbeeLayout = new QHBoxLayout; |
|
xbeeLayout->addStretch(); |
|
xbeeLayout->addLayout(tmpLayout); |
|
this->setLayout(xbeeLayout); |
|
|
|
//connect(portBox,SIGNAL(activated(QString)),this,SLOT(setPortName(QString))); |
|
//connect(baudBox,SIGNAL(activated(QString)),this,SLOT(setBaudRateString(QString))); |
|
connect(portBox,SIGNAL(currentIndexChanged(QString)),this,SLOT(setPortName(QString))); |
|
connect(portBox,SIGNAL(editTextChanged(QString)),this,SLOT(setPortName(QString))); |
|
connect(baudBox,SIGNAL(currentIndexChanged(QString)),this,SLOT(setBaudRateString(QString))); |
|
connect(highAddr,SIGNAL(valueChanged(int)),this,SLOT(addrChangedHigh(int))); |
|
connect(lowAddr,SIGNAL(valueChanged(int)),this,SLOT(addrChangedLow(int))); |
|
connect(this,SIGNAL(addrHighChanged(quint32)),xbeeLink,SLOT(setRemoteAddressHigh(quint32))); |
|
connect(this,SIGNAL(addrLowChanged(quint32)),xbeeLink,SLOT(setRemoteAddressLow(quint32))); |
|
|
|
baudBox->addItem("1200",1200); |
|
baudBox->addItem("2400",2400); |
|
baudBox->addItem("4800",4800); |
|
baudBox->addItem("9600",9600); |
|
baudBox->addItem("19200",19200); |
|
baudBox->addItem("38400",38400); |
|
baudBox->addItem("57600",57600); |
|
baudBox->setCurrentIndex(6); |
|
|
|
// try to open xbeeConf file for last remote address |
|
QFile in("Xbeeconf.txt"); |
|
if(in.open(QIODevice::ReadOnly)) |
|
{ |
|
QDataStream inStr(&in); |
|
int tmpaddrHigh; |
|
int tmpaddrLow; |
|
inStr >> tmpaddrHigh; |
|
inStr >> tmpaddrLow; |
|
highAddr->setValue(tmpaddrHigh); |
|
lowAddr->setValue(tmpaddrLow); |
|
} |
|
else |
|
{ |
|
highAddr->setValue(0x13A200); |
|
lowAddr->setValue(0x40DDDDDD); |
|
} |
|
|
|
|
|
|
|
this->setupPortList(); |
|
|
|
portCheckTimer = new QTimer(this); |
|
portCheckTimer->setInterval(1000); |
|
connect(portCheckTimer, SIGNAL(timeout()), this, SLOT(setupPortList())); |
|
|
|
// Display the widget |
|
this->window()->setWindowTitle(tr("Xbee Communication Settings")); |
|
} |
|
else |
|
{ |
|
qDebug() << "This is not a Xbee Link"; |
|
} |
|
} |
|
|
|
XbeeConfigurationWindow::~XbeeConfigurationWindow() |
|
{ |
|
|
|
} |
|
|
|
void XbeeConfigurationWindow::setupPortList() |
|
{ |
|
#ifdef __linux |
|
|
|
// TODO Linux has no standard way of enumerating serial ports |
|
// However the device files are only present when the physical |
|
// device is connected, therefore listing the files should be |
|
// sufficient. |
|
|
|
QString devdir = "/dev"; |
|
QDir dir(devdir); |
|
dir.setFilter(QDir::System); |
|
|
|
QFileInfoList list = dir.entryInfoList(); |
|
for (int i = 0; i < list.size(); ++i) { |
|
QFileInfo fileInfo = list.at(i); |
|
if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS"))) { |
|
if (portBox->findText(fileInfo.canonicalFilePath()) == -1) { |
|
portBox->addItem(fileInfo.canonicalFilePath()); |
|
if (!userConfigured) portBox->setEditText(fileInfo.canonicalFilePath()); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
#if defined (__APPLE__) && defined (__MACH__) |
|
|
|
// Enumerate serial ports |
|
//int fileDescriptor; |
|
kern_return_t kernResult; // on PowerPC this is an int (4 bytes) |
|
|
|
io_iterator_t serialPortIterator; |
|
char bsdPath[MAXPATHLEN]; |
|
|
|
kernResult = FindModems(&serialPortIterator); |
|
|
|
kernResult = GetModemPath(serialPortIterator, bsdPath, sizeof(bsdPath)); |
|
|
|
IOObjectRelease(serialPortIterator); // Release the iterator. |
|
|
|
// Add found modems |
|
if (bsdPath[0]) { |
|
if (portBox->findText(QString(bsdPath)) == -1) { |
|
portBox->addItem(QString(bsdPath)); |
|
if (!userConfigured) portBox->setEditText(QString(bsdPath)); |
|
} |
|
} |
|
|
|
// Add USB serial port adapters |
|
// TODO Strangely usb serial port adapters are not enumerated, even when connected |
|
QString devdir = "/dev"; |
|
QDir dir(devdir); |
|
dir.setFilter(QDir::System); |
|
|
|
QFileInfoList list = dir.entryInfoList(); |
|
for (int i = list.size() - 1; i >= 0; i--) { |
|
QFileInfo fileInfo = list.at(i); |
|
if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS")) || fileInfo.fileName().contains(QString("tty.usbserial"))) { |
|
if (portBox->findText(fileInfo.canonicalFilePath()) == -1) { |
|
portBox->addItem(fileInfo.canonicalFilePath()); |
|
if (!userConfigured) portBox->setEditText(fileInfo.canonicalFilePath()); |
|
} |
|
} |
|
} |
|
|
|
|
|
#endif |
|
|
|
#ifdef _WIN32 |
|
// Get the ports available on this system |
|
QList<QextPortInfo> ports = QextSerialEnumerator::getPorts(); |
|
|
|
// Add the ports in reverse order, because we prepend them to the list |
|
for (int i = ports.size() - 1; i >= 0; i--) { |
|
QString portString = QString(ports.at(i).portName.toLocal8Bit().constData()) + " - " + QString(ports.at(i).friendName.toLocal8Bit().constData()).split("(").first(); |
|
// Prepend newly found port to the list |
|
if (portBox->findText(portString) == -1) { |
|
portBox->insertItem(0, portString); |
|
if (!userConfigured) portBox->setEditText(portString); |
|
} |
|
} |
|
|
|
//printf("port name: %s\n", ports.at(i).portName.toLocal8Bit().constData()); |
|
//printf("friendly name: %s\n", ports.at(i).friendName.toLocal8Bit().constData()); |
|
//printf("physical name: %s\n", ports.at(i).physName.toLocal8Bit().constData()); |
|
//printf("enumerator name: %s\n", ports.at(i).enumName.toLocal8Bit().constData()); |
|
//printf("===================================\n\n"); |
|
#endif |
|
} |
|
|
|
void XbeeConfigurationWindow::showEvent(QShowEvent* event) |
|
{ |
|
Q_UNUSED(event); |
|
portCheckTimer->start(); |
|
} |
|
|
|
void XbeeConfigurationWindow::hideEvent(QHideEvent* event) |
|
{ |
|
Q_UNUSED(event); |
|
portCheckTimer->stop(); |
|
} |
|
|
|
QAction* XbeeConfigurationWindow::getAction() |
|
{ |
|
return action; |
|
} |
|
|
|
void XbeeConfigurationWindow::configureCommunication() |
|
{ |
|
this->setupPortList(); |
|
this->show(); |
|
} |
|
|
|
void XbeeConfigurationWindow::setPortName(QString port) |
|
{ |
|
link->setPortName(port); |
|
} |
|
|
|
void XbeeConfigurationWindow::setBaudRateString(QString baud) |
|
{ |
|
int rate = baud.toInt(); |
|
this->link->setBaudRate(rate); |
|
} |
|
|
|
void XbeeConfigurationWindow::addrChangedHigh(int addr) |
|
{ |
|
quint32 uaddr = static_cast<quint32>(addr); |
|
QFile out("Xbeeconf.txt"); |
|
if(out.open(QIODevice::WriteOnly)) |
|
{ |
|
QDataStream outStr(&out); |
|
outStr << this->highAddr->value(); |
|
outStr << this->lowAddr->value(); |
|
} |
|
emit addrHighChanged(uaddr); |
|
} |
|
|
|
void XbeeConfigurationWindow::addrChangedLow(int addr) |
|
{ |
|
quint32 uaddr = static_cast<quint32>(addr); |
|
QFile out("Xbeeconf.txt"); |
|
if(out.open(QIODevice::WriteOnly)) |
|
{ |
|
QDataStream outStr(&out); |
|
outStr << this->highAddr->value(); |
|
outStr << this->lowAddr->value(); |
|
} |
|
emit addrLowChanged(uaddr); |
|
} |