diff --git a/AVRsack/ASBuilder.swift b/AVRsack/ASBuilder.swift index 880aeba..a468922 100644 --- a/AVRsack/ASBuilder.swift +++ b/AVRsack/ASBuilder.swift @@ -3,7 +3,7 @@ // AVRsack // // Created by Matthias Neeracher on 11/24/14. -// Copyright © 2014 Aere Perennius. All rights reserved. +// Copyright © 2014-2015 Aere Perennius. All rights reserved. // import Foundation @@ -99,18 +99,29 @@ class ASBuilder { task!.launch() } - func uploadProject(board: String, programmer: String, port: String) { + func uploadProject(board: String, programmer: String, port: String, terminal: Bool = false) { + let portPath = ASSerial.fileNameForPort(port) let toolChain = (NSApplication.sharedApplication().delegate as ASApplication).preferences.toolchainPath task = NSTask() task!.currentDirectoryPath = dir.path! task!.launchPath = toolChain+"/bin/avrdude" let fileManager = NSFileManager.defaultManager() - let logURL = dir.URLByAppendingPathComponent("build/upload.log") - fileManager.createFileAtPath(logURL.path!, contents: NSData(), attributes: nil) - let logOut = NSFileHandle(forWritingAtPath: logURL.path!)! - task!.standardOutput = logOut - task!.standardError = logOut + var logOut : NSFileHandle + if terminal { + let inputPipe = NSPipe() + let outputPipe = NSPipe() + logOut = outputPipe.fileHandleForWriting + task!.standardInput = inputPipe + task!.standardOutput = outputPipe + task!.standardError = outputPipe + } else { + let logURL = dir.URLByAppendingPathComponent("build/upload.log") + fileManager.createFileAtPath(logURL.path!, contents: NSData(), attributes: nil) + logOut = NSFileHandle(forWritingAtPath: logURL.path!)! + task!.standardOutput = logOut + task!.standardError = logOut + } let libPath = (ASLibraries.instance().directories as NSArray).componentsJoinedByString(":") let boardProp = ASHardware.instance().boards[board]! @@ -123,8 +134,12 @@ class ASBuilder { var args = Array(count: verbosity, repeatedValue: "-v") args += [ "-C", toolChain+"/etc/avrdude.conf", - "-p", boardProp["build.mcu"]!, "-c", proto!, "-P", port, - "-U", "flash:w:build/"+board+"/"+dir.lastPathComponent+".hex:i"] + "-p", boardProp["build.mcu"]!, "-c", proto!, "-P", portPath] + if terminal { + args += ["-t"] + } else { + args += ["-U", "flash:w:build/"+board+"/"+dir.lastPathComponent+".hex:i"] + } if speed != nil { args.append("-b") args.append(speed!) @@ -135,13 +150,15 @@ class ASBuilder { // if leonardish { if verbosity > 0 { - logOut.writeData("Opening \(port) at 1200 baud\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) + logOut.writeData("Opening port \(port) at 1200 baud\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) } - if let dummyConnection = ASSerial.openPort(port, withSpeed: 1200) { - close(dummyConnection.fileDescriptor) + if let dummyConnection = ASSerial.openPort(portPath, withSpeed: 1200) { + usleep(50000) + ASSerial.closePort(dummyConnection.fileDescriptor) + sleep(1) for (var retry=0; retry < 40; ++retry) { usleep(250000) - if (NSFileManager.defaultManager().fileExistsAtPath(port)) { + if (fileManager.fileExistsAtPath(portPath)) { if verbosity > 0 { logOut.writeData("Found port \(port) after \(retry) attempts.\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) } @@ -154,6 +171,11 @@ class ASBuilder { logOut.writeData(cmdLine.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) task!.arguments = args; task!.launch() + if terminal { + let intSpeed = speed?.toInt() ?? 19200 + ASSerialWin.showWindowWithPort(port, task:task!, speed:intSpeed) + task = nil + } } func disassembleProject(board: String) { diff --git a/AVRsack/ASProjDoc.swift b/AVRsack/ASProjDoc.swift index 16ac2b3..0274e1a 100644 --- a/AVRsack/ASProjDoc.swift +++ b/AVRsack/ASProjDoc.swift @@ -475,12 +475,16 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate { }) } dispatch_async(dispatch_get_main_queue(), { - self.builder.uploadProject(self.board, programmer:self.programmer, port:ASSerial.fileNameForPort(self.port)) + self.builder.uploadProject(self.board, programmer:self.programmer, port:self.port) }) } buildProject(sender) } - + + @IBAction func uploadTerminal(sender: AnyObject) { + builder.uploadProject(board, programmer:programmer, port:port, terminal:true) + } + @IBAction func disassembleProject(sender: AnyObject) { builder.continuation = { self.selectNodeInOutline(self.files.disassembly) diff --git a/AVRsack/ASSerial.h b/AVRsack/ASSerial.h index bbfe17d..1a6182f 100644 --- a/AVRsack/ASSerial.h +++ b/AVRsack/ASSerial.h @@ -22,5 +22,6 @@ extern NSString * kASSerialPortsChanged; + (NSArray *) ports; + (NSFileHandle *)openPort:(NSString *) port withSpeed:(int)speed; + (void)restorePort:(int)fileDescriptor; ++ (void)closePort:(int)fileDescriptor; @end diff --git a/AVRsack/ASSerial.mm b/AVRsack/ASSerial.mm index 32ac53e..4f3a9ea 100644 --- a/AVRsack/ASSerial.mm +++ b/AVRsack/ASSerial.mm @@ -79,7 +79,6 @@ NSString * kASSerialPortsChanged = @"PortsChanged"; savedAttrs = [NSMutableDictionary dictionary]; } [savedAttrs setObject:[NSData dataWithBytes:&origAttr length:sizeof(origAttr)] forKey:[NSNumber numberWithInt:fd]]; - return [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]; failed: close(fd); @@ -95,4 +94,9 @@ failed: } } ++ (void)closePort:(int)fileDescriptor { + NSNumber * fd = [NSNumber numberWithInt:fileDescriptor]; + close(fileDescriptor); + [savedAttrs removeObjectForKey:fd]; +} @end diff --git a/AVRsack/ASSerialWin.swift b/AVRsack/ASSerialWin.swift index 9ba6940..4f6058c 100644 --- a/AVRsack/ASSerialWin.swift +++ b/AVRsack/ASSerialWin.swift @@ -37,12 +37,14 @@ class ASSerialWin: NSWindowController { var port = "" var serialData = "" var serialObserver : AnyObject! + var termination : AnyObject! dynamic var portHandle : NSFileHandle? var currentTheme : UInt = 0 var fontSize : UInt = 12 var portDefaults = [String: AnyObject]() var shouldReconnect = false - + dynamic var task : NSTask? + class func showWindowWithPort(port: String) { if let existing = serialInstances[port] { existing.showWindow(self) @@ -52,6 +54,15 @@ class ASSerialWin: NSWindowController { newInstance.showWindow(self) } } + class func showWindowWithPort(port: String, task: NSTask, speed: Int) { + if let existing = serialInstances[port] { + existing.showWindowWithTask(task, speed:speed) + } else { + let newInstance = ASSerialWin(port:port) + serialInstances[port] = newInstance + newInstance.showWindowWithTask(task, speed:speed) + } + } class func portNeededForUpload(port: String) { if let existing = serialInstances[port] { existing.disconnectTemporarily() @@ -97,18 +108,38 @@ class ASSerialWin: NSWindowController { self.willChangeValueForKey("hasValidPort") self.didChangeValueForKey("hasValidPort") - if self.hasValidPort { - self.reconnect() - } else { - self.disconnectTemporarily() + if self.task == nil { + if self.hasValidPort { + self.reconnect() + } else { + self.disconnectTemporarily() + } } }) + termination = NSNotificationCenter.defaultCenter().addObserverForName(NSTaskDidTerminateNotification, + object: nil, queue: nil, usingBlock: + { (notification: NSNotification!) in + if notification.object as? NSTask == self.task { + self.task = nil + self.portHandle = nil + } + }) } override func finalize() { + if portHandle != nil { + connect(self) + } NSNotificationCenter.defaultCenter().removeObserver(serialObserver) + NSNotificationCenter.defaultCenter().removeObserver(termination) serialInstances.removeValueForKey(port) } + + func windowWillClose(notification: NSNotification) { + if portHandle != nil { + connect(self) + } + } override func windowDidLoad() { logView.setReadOnly(true) @@ -119,13 +150,28 @@ class ASSerialWin: NSWindowController { logView.setMode(UInt(ACEModeText)) logView.alphaValue = 0.8 window?.title = port - connect(self) + if task == nil { + connect(self) + } super.windowDidLoad() } - - @IBAction func selectPort(item: AnyObject) { - port = (item as NSPopUpButton).titleOfSelectedItem! - window?.title = port + + func installReader(handle: NSFileHandle?) { + if let readHandle = handle { + serialData = "" + logView.setString(serialData) + readHandle.readabilityHandler = {(handle) in + let newData = handle.availableDataIgnoringExceptions() + let newString = NSString(data: newData, encoding: NSASCIIStringEncoding)! + self.serialData += newString + dispatch_async(dispatch_get_main_queue(), { () -> Void in + self.logView.setString(self.serialData) + if self.scrollToBottom { + self.logView.gotoLine(1000000000, column: 0, animated: true) + } + }) + } + } } @IBAction func sendInput(AnyObject) { @@ -134,29 +180,29 @@ class ASSerialWin: NSWindowController { portHandle?.writeData(data) } + func showWindowWithTask(task: NSTask, speed:Int) { + if portHandle != nil { + connect(self) + } + baudRate = speed + self.task = task + portHandle = (task.standardInput as NSPipe).fileHandleForWriting + showWindow(self) + installReader((task.standardOutput as? NSPipe)?.fileHandleForReading) + } + @IBAction func connect(AnyObject) { shouldReconnect = false - if portHandle != nil { - ASSerial.restorePort(portHandle!.fileDescriptor) - portHandle!.closeFile() + if task != nil { + task!.interrupt() + } else if portHandle != nil { + let fd = portHandle!.fileDescriptor + ASSerial.restorePort(fd) + ASSerial.closePort(fd) portHandle = nil } else { portHandle = ASSerial.openPort(port, withSpeed: Int32(baudRate)) - if portHandle != nil { - serialData = "" - logView.setString(serialData) - portHandle!.readabilityHandler = {(handle) in - let newData = handle.availableDataIgnoringExceptions() - let newString = NSString(data: newData, encoding: NSASCIIStringEncoding)! - self.serialData += newString - dispatch_async(dispatch_get_main_queue(), { () -> Void in - self.logView.setString(self.serialData) - if self.scrollToBottom { - self.logView.gotoLine(1000000000, column: 0, animated: true) - } - }) - } - } + installReader(portHandle) } } func disconnectTemporarily() { @@ -234,7 +280,7 @@ class ASSerialWin: NSWindowController { func updatePortDefaults() { let userDefaults = NSUserDefaults.standardUserDefaults() let serialDefaults = NSMutableDictionary(dictionary:userDefaults.objectForKey("SerialDefaults") as NSDictionary) - serialDefaults.setValue(portDefaults, forKey:port) + serialDefaults.setValue(NSDictionary(dictionary:portDefaults), forKey:port) userDefaults.setObject(serialDefaults, forKey:"SerialDefaults") } diff --git a/AVRsack/ASSerialWin.xib b/AVRsack/ASSerialWin.xib index b4964b7..e6e18d5 100644 --- a/AVRsack/ASSerialWin.xib +++ b/AVRsack/ASSerialWin.xib @@ -13,20 +13,17 @@ - + - + - + - - - @@ -42,12 +39,12 @@