From fb9576c8c006ff18af9d1389b640e9ce72211404 Mon Sep 17 00:00:00 2001 From: PaulStoffregen Date: Tue, 15 Mar 2016 15:32:15 -0700 Subject: [PATCH] Add Port menu to GUI --- Makefile | 3 +- gui.cpp | 85 +++++++++++++-- gui.h | 13 +++ portlist.cpp | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 385 insertions(+), 8 deletions(-) create mode 100644 portlist.cpp diff --git a/Makefile b/Makefile index c3dd7d0..cd97a38 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ OBJS = visualize.o serialdata.o rawdata.o magcal.o matrix.o fusion.o all: $(ALL) -gui: gui.o $(OBJS) +gui: gui.o portlist.o $(OBJS) $(CXX) -s $(CFLAGS) $(LDFLAGS) -o $@ $^ `$(WXCONFIG) --libs all,opengl` gui.exe: gui @@ -62,6 +62,7 @@ clean: rm -rf gui.app .DS_Store gui.o: gui.cpp gui.h imuread.h +portlist.o: portlist.cpp gui.h imuread.o: imuread.c imuread.h visualize.o: visualize.c imuread.h serialdata.o: serialdata.c imuread.h diff --git a/gui.cpp b/gui.cpp index 9dbd24f..029d9b5 100644 --- a/gui.cpp +++ b/gui.cpp @@ -3,14 +3,15 @@ - +wxMenu *port_menu; +wxString port_name; wxBEGIN_EVENT_TABLE(MyCanvas, wxGLCanvas) - EVT_SIZE(MyCanvas::OnSize) - EVT_PAINT(MyCanvas::OnPaint) - //EVT_CHAR(MyCanvas::OnChar) - //EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent) + EVT_SIZE(MyCanvas::OnSize) + EVT_PAINT(MyCanvas::OnPaint) + //EVT_CHAR(MyCanvas::OnChar) + //EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent) wxEND_EVENT_TABLE() MyCanvas::MyCanvas(wxWindow *parent, wxWindowID id, int* gl_attrib) @@ -60,6 +61,9 @@ BEGIN_EVENT_TABLE(MyFrame,wxFrame) EVT_MENU(wxID_ABOUT, MyFrame::OnAbout) EVT_MENU(wxID_EXIT, MyFrame::OnQuit) EVT_TIMER(ID_TIMER, MyFrame::OnTimer) + EVT_MENU_RANGE(9000, 9999, MyFrame::OnPort) + EVT_MENU_OPEN(MyMenu::OnShowPortList) + EVT_MENU_HIGHLIGHT(-1, MyMenu::OnHighlight) END_EVENT_TABLE() @@ -75,6 +79,11 @@ MyFrame::MyFrame(wxWindow *parent, wxWindowID id, const wxString &title, menu = new wxMenu; menu->Append(wxID_EXIT, wxT("Quit")); menuBar->Append(menu, wxT("&File")); + + menu = new wxMenu; + menuBar->Append(menu, "Port"); + port_menu = menu; + menu = new wxMenu; //item = new wxMenuItem(menu, ID_ABOUT, "About"); menu->Append(wxID_ABOUT, wxT("About")); @@ -105,7 +114,8 @@ MyFrame::MyFrame(wxWindow *parent, wxWindowID id, const wxString &title, Raise(); m_canvas->InitGL(); - open_port(PORT); + raw_data_reset(); + //open_port(PORT); m_timer = new wxTimer(this, ID_TIMER); m_timer->Start(40, wxTIMER_CONTINUOUS); } @@ -117,6 +127,21 @@ void MyFrame::OnTimer(wxTimerEvent &event) m_canvas->Refresh(); } + +void MyFrame::OnPort(wxCommandEvent &event) +{ + int id = event.GetId(); + wxString name = port_menu->FindItem(id)->GetItemLabelText(); + + close_port(); + //printf("OnPort, id = %d, name = %s\n", id, (const char *)name); + port_name = name; + if (id == 9000) return; + raw_data_reset(); + open_port((const char *)name); +} + + void MyFrame::OnAbout(wxCommandEvent &event) { wxMessageDialog dialog(this, @@ -137,7 +162,49 @@ MyFrame::~MyFrame(void) /*****************************************************************************/ +// Port Menu +MyMenu::MyMenu(const wxString& title, long style) : wxMenu(title, style) +{ +} + +void MyMenu::OnShowPortList(wxMenuEvent &event) +{ + wxMenu *menu; + int any=0; + int num; + + menu = event.GetMenu(); + //printf("OnShowPortList, %s\n", (const char *)menu->GetTitle()); + if (menu != port_menu) return; + + wxMenuItemList old_items = menu->GetMenuItems(); + num = old_items.GetCount(); + for (int i = old_items.GetCount() - 1; i >= 0; i--) { + menu->Delete(old_items[i]); + } + menu->AppendRadioItem(9000, " (none)"); + wxArrayString list = serial_port_list(); + num = list.GetCount(); + for (int i=0; i < num; i++) { + //printf("%d: port %s\n", i, (const char *)list[i]); + menu->AppendRadioItem(9001 + i, list[i]); + if (port_name.IsSameAs(list[i])) { + menu->Check(9001 + i, true); + any = 1; + } + } + if (!any) menu->Check(9000, true); +} + +void MyMenu::OnHighlight(wxMenuEvent &event) +{ + //printf("OnHighlight\n"); +} + + + +/*****************************************************************************/ IMPLEMENT_APP(MyApp) @@ -152,7 +219,7 @@ bool MyApp::OnInit() wxPoint pos(100, 100); - MyFrame *frame = new MyFrame(NULL, -1, wxT("IMU Read"), pos, wxSize(1160,660), + MyFrame *frame = new MyFrame(NULL, -1, wxT("IMU Read"), pos, wxSize(1120,760), wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)); frame->Show( true ); return true; @@ -162,3 +229,7 @@ int MyApp::OnExit() { return 0; } + + + + diff --git a/gui.h b/gui.h index 3c05665..14c25f9 100644 --- a/gui.h +++ b/gui.h @@ -61,6 +61,7 @@ public: long style = wxDEFAULT_FRAME_STYLE); ~MyFrame(void); void InitGL(); + void OnPort(wxCommandEvent &event); private: MyCanvas *m_canvas; wxTimer *m_timer; @@ -70,6 +71,14 @@ private: DECLARE_EVENT_TABLE() }; +class MyMenu: public wxMenu +{ +public: + MyMenu(const wxString& title = "", long style = 0); + void OnShowPortList(wxMenuEvent &event); + void OnHighlight(wxMenuEvent &event); +}; + class MyApp: public wxApp { @@ -81,4 +90,8 @@ private: //wxSingleInstanceChecker *m_instance; }; +// portlist.cpp +wxArrayString serial_port_list(); + + #endif diff --git a/portlist.cpp b/portlist.cpp new file mode 100644 index 0000000..d74b2f8 --- /dev/null +++ b/portlist.cpp @@ -0,0 +1,292 @@ +/* Serial port object for use with wxWidgets + * Copyright 2010, Paul Stoffregen (paul@pjrc.com) + */ + +#include "gui.h" + +#if defined(LINUX) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#elif defined(MACOSX) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#elif defined(WINDOWS) + #include + #define win32_err(s) FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, \ + GetLastError(), 0, (s), sizeof(s), NULL) + #define QUERYDOSDEVICE_BUFFER_SIZE 262144 +#else + #error "This platform is unsupported, sorry" +#endif + + + +#if defined(LINUX) +// All linux serial port device names. Hopefully all of them anyway. This +// is a long list, but each entry takes only a few bytes and a quick strcmp() +static const char *devnames[] = { +"S", // "normal" Serial Ports - MANY drivers using this +"USB", // USB to serial converters +"ACM", // USB serial modem, CDC class, Abstract Control Model +"MI", // MOXA Smartio/Industio family multiport serial... nice card, I have one :-) +"MX", // MOXA Intellio family multiport serial +"C", // Cyclades async multiport, no longer available, but I have an old ISA one! :-) +"D", // Digiboard (still in 2.6 but no longer supported), new Moschip MCS9901 +"P", // Hayes ESP serial cards (obsolete) +"M", // PAM Software's multimodem & Multitech ISI-Cards +"E", // Stallion intelligent multiport (no longer made) +"L", // RISCom/8 multiport serial +"W", // specialix IO8+ multiport serial +"X", // Specialix SX series cards, also SI & XIO series +"SR", // Specialix RIO serial card 257+ +"n", // Digi International Neo (yes lowercase 'n', drivers/serial/jsm/jsm_driver.c) +"FB", // serial port on the 21285 StrongArm-110 core logic chip +"AM", // ARM AMBA-type serial ports (no DTR/RTS) +"AMA", // ARM AMBA-type serial ports (no DTR/RTS) +"AT", // Atmel AT91 / AT32 Serial ports +"BF", // Blackfin 5xx serial ports (Analog Devices embedded DSP chips) +"CL", // CLPS711x serial ports (ARM processor) +"A", // ICOM Serial +"SMX", // Motorola IMX serial ports +"SOIC", // ioc3 serial +"IOC", // ioc4 serial +"PSC", // Freescale MPC52xx PSCs configured as UARTs +"MM", // MPSC (UART mode) on Marvell GT64240, GT64260, MV64340... +"B", // Mux console found in some PA-RISC servers +"NX", // NetX serial port +"PZ", // PowerMac Z85c30 based ESCC cell found in the "macio" ASIC +"SAC", // Samsung S3C24XX onboard UARTs +"SA", // SA11x0 serial ports +"AM", // KS8695 serial ports & Sharp LH7A40X embedded serial ports +"TX", // TX3927/TX4927/TX4925/TX4938 internal SIO controller +"SC", // Hitachi SuperH on-chip serial module +"SG", // C-Brick Serial Port (and console) SGI Altix machines +"HV", // SUN4V hypervisor console +"UL", // Xilinx uartlite serial controller +"VR", // NEC VR4100 series Serial Interface Unit +"CPM", // CPM (SCC/SMC) serial ports; core driver +"Y", // Amiga A2232 board +"SL", // Microgate SyncLink ISA and PCI high speed multiprotocol serial +"SLG", // Microgate SyncLink GT (might be sync HDLC only?) +"SLM", // Microgate SyncLink Multiport high speed multiprotocol serial +"CH", // Chase Research AT/PCI-Fast serial card +"F", // Computone IntelliPort serial card +"H", // Chase serial card +"I", // virtual modems +"R", // Comtrol RocketPort +"SI", // SmartIO serial card +"T", // Technology Concepts serial card +"V" // Comtrol VS-1000 serial controller +}; +#define NUM_DEVNAMES (sizeof(devnames) / sizeof(const char *)) +#endif + + + +#if defined(MACOSX) +static void macos_ports(io_iterator_t * PortIterator, wxArrayString& list) +{ + io_object_t modemService; + CFTypeRef nameCFstring; + char s[MAXPATHLEN]; + + while ((modemService = IOIteratorNext(*PortIterator))) { + nameCFstring = IORegistryEntryCreateCFProperty(modemService, + CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (nameCFstring) { + if (CFStringGetCString((const __CFString *)nameCFstring, + s, sizeof(s), kCFStringEncodingASCII)) { + list.Add(s); + } + CFRelease(nameCFstring); + } + IOObjectRelease(modemService); + } +} +#endif + + +// Return a list of all serial ports +wxArrayString serial_port_list() +{ + wxArrayString list; +#if defined(LINUX) + // This is ugly guessing, but Linux doesn't seem to provide anything else. + // If there really is an API to discover serial devices on Linux, please + // email paul@pjrc.com with the info. Please? + // The really BAD aspect is all ports get DTR raised briefly, because linux + // has no way to open the port without raising DTR, and there isn't any way + // to tell if the device file really represents hardware without opening it. + // maybe sysfs or udev provides a useful API?? + DIR *dir; + struct dirent *f; + struct stat st; + unsigned int i, len[NUM_DEVNAMES]; + char s[512]; + int fd, bits; + termios mytios; + + dir = opendir("/dev/"); + if (dir == NULL) return list; + for (i=0; id_name, "tty", 3)) continue; + // ignore anything that's not a known serial device name + for (i=0; id_name + 3, devnames[i], len[i])) break; + } + if (i >= NUM_DEVNAMES) continue; + snprintf(s, sizeof(s), "/dev/%s", f->d_name); + // check if it's a character type device (almost certainly is) + if (stat(s, &st) != 0 || !(st.st_mode & S_IFCHR)) continue; + // now see if we can open the file - if the device file is + // populating /dev but doesn't actually represent a loaded + // driver, this is where we will detect it. + fd = open(s, O_RDONLY | O_NOCTTY | O_NONBLOCK); + if (fd < 0) { + // if permission denied, give benefit of the doubt + // (otherwise the port will be invisible to the user + // and we won't have a to alert them to the permssion + // problem) + if (errno == EACCES) list.Add(s); + // any other error, assume it's not a real device + continue; + } + // does it respond to termios requests? (probably will since + // the name began with tty). Some devices where a single + // driver exports multiple names will open but this is where + // we can really tell if they work with real hardare. + if (tcgetattr(fd, &mytios) != 0) { + close(fd); + continue; + } + // does it respond to reading the control signals? If it's + // some sort of non-serial terminal (eg, pseudo terminals) + // this is where we will detect it's not really a serial port + if (ioctl(fd, TIOCMGET, &bits) < 0) { + close(fd); + continue; + } + // it passed all the tests, it's a serial port, or some sort + // of "terminal" that looks exactly like a real serial port! + close(fd); + // unfortunately, Linux always raises DTR when open is called. + // not nice! Every serial port is going to get DTR raised + // and then lowered. I wish there were a way to prevent this, + // but it seems impossible. + list.Add(s); + } + closedir(dir); +#elif defined(MACOSX) + // adapted from SerialPortSample.c, by Apple + // http://developer.apple.com/samplecode/SerialPortSample/listing2.html + // and also testserial.c, by Keyspan + // http://www.keyspan.com/downloads-files/developer/macosx/KesypanTestSerial.c + // www.rxtx.org, src/SerialImp.c seems to be based on Keyspan's testserial.c + // neither keyspan nor rxtx properly release memory allocated. + // more documentation at: + // http://developer.apple.com/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/chapter_2_section_6.html + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + io_iterator_t serialPortIterator; + if (IOMasterPort(NULL, &masterPort) != KERN_SUCCESS) return list; + // a usb-serial adaptor is usually considered a "modem", + // especially when it implements the CDC class spec + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) return list; + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDModemType)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, + &serialPortIterator) != KERN_SUCCESS) return list; + macos_ports(&serialPortIterator, list); + IOObjectRelease(serialPortIterator); + // but it might be considered a "rs232 port", so repeat this + // search for rs232 ports + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) return list; + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDRS232Type)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, + &serialPortIterator) != KERN_SUCCESS) return list; + macos_ports(&serialPortIterator, list); + IOObjectRelease(serialPortIterator); +#elif defined(WINDOWS) + // http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx + // page with 7 ways - not all of them work! + // http://www.naughter.com/enumser.html + // may be possible to just query the windows registary + // http://it.gps678.com/2/ca9c8631868fdd65.html + // search in HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM + // Vista has some special new way, vista-only + // http://msdn2.microsoft.com/en-us/library/aa814070(VS.85).aspx + char *buffer, *p; + //DWORD size = QUERYDOSDEVICE_BUFFER_SIZE; + DWORD ret; + + buffer = (char *)malloc(QUERYDOSDEVICE_BUFFER_SIZE); + if (buffer == NULL) return list; + memset(buffer, 0, QUERYDOSDEVICE_BUFFER_SIZE); + ret = QueryDosDeviceA(NULL, buffer, QUERYDOSDEVICE_BUFFER_SIZE); + if (ret) { + printf("Detect Serial using QueryDosDeviceA: "); + for (p = buffer; *p; p += strlen(p) + 1) { + printf(": %s", p); + if (strncmp(p, "COM", 3)) continue; + list.Add(wxString(p) + ":"); + } + } else { + char buf[1024]; + win32_err(buf); + printf("QueryDosDeviceA failed, error \"%s\"\n", buf); + printf("Detect Serial using brute force GetDefaultCommConfig probing: "); + for (int i=1; i<=32; i++) { + printf("try %s", buf); + COMMCONFIG cfg; + DWORD len; + snprintf(buf, sizeof(buf), "COM%d", i); + if (GetDefaultCommConfig(buf, &cfg, &len)) { + wxString name; + name.Printf("COM%d:", i); + list.Add(name); + printf(": %s", buf); + } + } + } + free(buffer); +#endif + list.Sort(); + return list; +} + + + + + + + +