Implement upload monitor, fix caterina timings

This commit is contained in:
Matthias Neeracher 2015-01-02 16:17:10 +01:00 committed by Matthias Neeracher
parent de93b9e2a9
commit 533471fba8
7 changed files with 141 additions and 59 deletions

View File

@ -3,7 +3,7 @@
// AVRsack // AVRsack
// //
// Created by Matthias Neeracher on 11/24/14. // 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 import Foundation
@ -99,18 +99,29 @@ class ASBuilder {
task!.launch() 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 let toolChain = (NSApplication.sharedApplication().delegate as ASApplication).preferences.toolchainPath
task = NSTask() task = NSTask()
task!.currentDirectoryPath = dir.path! task!.currentDirectoryPath = dir.path!
task!.launchPath = toolChain+"/bin/avrdude" task!.launchPath = toolChain+"/bin/avrdude"
let fileManager = NSFileManager.defaultManager() let fileManager = NSFileManager.defaultManager()
let logURL = dir.URLByAppendingPathComponent("build/upload.log") var logOut : NSFileHandle
fileManager.createFileAtPath(logURL.path!, contents: NSData(), attributes: nil) if terminal {
let logOut = NSFileHandle(forWritingAtPath: logURL.path!)! let inputPipe = NSPipe()
task!.standardOutput = logOut let outputPipe = NSPipe()
task!.standardError = logOut 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 libPath = (ASLibraries.instance().directories as NSArray).componentsJoinedByString(":")
let boardProp = ASHardware.instance().boards[board]! let boardProp = ASHardware.instance().boards[board]!
@ -123,8 +134,12 @@ class ASBuilder {
var args = Array<String>(count: verbosity, repeatedValue: "-v") var args = Array<String>(count: verbosity, repeatedValue: "-v")
args += [ args += [
"-C", toolChain+"/etc/avrdude.conf", "-C", toolChain+"/etc/avrdude.conf",
"-p", boardProp["build.mcu"]!, "-c", proto!, "-P", port, "-p", boardProp["build.mcu"]!, "-c", proto!, "-P", portPath]
"-U", "flash:w:build/"+board+"/"+dir.lastPathComponent+".hex:i"] if terminal {
args += ["-t"]
} else {
args += ["-U", "flash:w:build/"+board+"/"+dir.lastPathComponent+".hex:i"]
}
if speed != nil { if speed != nil {
args.append("-b") args.append("-b")
args.append(speed!) args.append(speed!)
@ -135,13 +150,15 @@ class ASBuilder {
// //
if leonardish { if leonardish {
if verbosity > 0 { 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) { if let dummyConnection = ASSerial.openPort(portPath, withSpeed: 1200) {
close(dummyConnection.fileDescriptor) usleep(50000)
ASSerial.closePort(dummyConnection.fileDescriptor)
sleep(1)
for (var retry=0; retry < 40; ++retry) { for (var retry=0; retry < 40; ++retry) {
usleep(250000) usleep(250000)
if (NSFileManager.defaultManager().fileExistsAtPath(port)) { if (fileManager.fileExistsAtPath(portPath)) {
if verbosity > 0 { if verbosity > 0 {
logOut.writeData("Found port \(port) after \(retry) attempts.\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) 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)!) logOut.writeData(cmdLine.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
task!.arguments = args; task!.arguments = args;
task!.launch() task!.launch()
if terminal {
let intSpeed = speed?.toInt() ?? 19200
ASSerialWin.showWindowWithPort(port, task:task!, speed:intSpeed)
task = nil
}
} }
func disassembleProject(board: String) { func disassembleProject(board: String) {

View File

@ -475,12 +475,16 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate {
}) })
} }
dispatch_async(dispatch_get_main_queue(), { 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) buildProject(sender)
} }
@IBAction func uploadTerminal(sender: AnyObject) {
builder.uploadProject(board, programmer:programmer, port:port, terminal:true)
}
@IBAction func disassembleProject(sender: AnyObject) { @IBAction func disassembleProject(sender: AnyObject) {
builder.continuation = { builder.continuation = {
self.selectNodeInOutline(self.files.disassembly) self.selectNodeInOutline(self.files.disassembly)

View File

@ -22,5 +22,6 @@ extern NSString * kASSerialPortsChanged;
+ (NSArray *) ports; + (NSArray *) ports;
+ (NSFileHandle *)openPort:(NSString *) port withSpeed:(int)speed; + (NSFileHandle *)openPort:(NSString *) port withSpeed:(int)speed;
+ (void)restorePort:(int)fileDescriptor; + (void)restorePort:(int)fileDescriptor;
+ (void)closePort:(int)fileDescriptor;
@end @end

View File

@ -79,7 +79,6 @@ NSString * kASSerialPortsChanged = @"PortsChanged";
savedAttrs = [NSMutableDictionary dictionary]; savedAttrs = [NSMutableDictionary dictionary];
} }
[savedAttrs setObject:[NSData dataWithBytes:&origAttr length:sizeof(origAttr)] forKey:[NSNumber numberWithInt:fd]]; [savedAttrs setObject:[NSData dataWithBytes:&origAttr length:sizeof(origAttr)] forKey:[NSNumber numberWithInt:fd]];
return [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]; return [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO];
failed: failed:
close(fd); close(fd);
@ -95,4 +94,9 @@ failed:
} }
} }
+ (void)closePort:(int)fileDescriptor {
NSNumber * fd = [NSNumber numberWithInt:fileDescriptor];
close(fileDescriptor);
[savedAttrs removeObjectForKey:fd];
}
@end @end

View File

@ -37,12 +37,14 @@ class ASSerialWin: NSWindowController {
var port = "" var port = ""
var serialData = "" var serialData = ""
var serialObserver : AnyObject! var serialObserver : AnyObject!
var termination : AnyObject!
dynamic var portHandle : NSFileHandle? dynamic var portHandle : NSFileHandle?
var currentTheme : UInt = 0 var currentTheme : UInt = 0
var fontSize : UInt = 12 var fontSize : UInt = 12
var portDefaults = [String: AnyObject]() var portDefaults = [String: AnyObject]()
var shouldReconnect = false var shouldReconnect = false
dynamic var task : NSTask?
class func showWindowWithPort(port: String) { class func showWindowWithPort(port: String) {
if let existing = serialInstances[port] { if let existing = serialInstances[port] {
existing.showWindow(self) existing.showWindow(self)
@ -52,6 +54,15 @@ class ASSerialWin: NSWindowController {
newInstance.showWindow(self) 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) { class func portNeededForUpload(port: String) {
if let existing = serialInstances[port] { if let existing = serialInstances[port] {
existing.disconnectTemporarily() existing.disconnectTemporarily()
@ -97,18 +108,38 @@ class ASSerialWin: NSWindowController {
self.willChangeValueForKey("hasValidPort") self.willChangeValueForKey("hasValidPort")
self.didChangeValueForKey("hasValidPort") self.didChangeValueForKey("hasValidPort")
if self.hasValidPort { if self.task == nil {
self.reconnect() if self.hasValidPort {
} else { self.reconnect()
self.disconnectTemporarily() } 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() { override func finalize() {
if portHandle != nil {
connect(self)
}
NSNotificationCenter.defaultCenter().removeObserver(serialObserver) NSNotificationCenter.defaultCenter().removeObserver(serialObserver)
NSNotificationCenter.defaultCenter().removeObserver(termination)
serialInstances.removeValueForKey(port) serialInstances.removeValueForKey(port)
} }
func windowWillClose(notification: NSNotification) {
if portHandle != nil {
connect(self)
}
}
override func windowDidLoad() { override func windowDidLoad() {
logView.setReadOnly(true) logView.setReadOnly(true)
@ -119,13 +150,28 @@ class ASSerialWin: NSWindowController {
logView.setMode(UInt(ACEModeText)) logView.setMode(UInt(ACEModeText))
logView.alphaValue = 0.8 logView.alphaValue = 0.8
window?.title = port window?.title = port
connect(self) if task == nil {
connect(self)
}
super.windowDidLoad() super.windowDidLoad()
} }
@IBAction func selectPort(item: AnyObject) { func installReader(handle: NSFileHandle?) {
port = (item as NSPopUpButton).titleOfSelectedItem! if let readHandle = handle {
window?.title = port 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) { @IBAction func sendInput(AnyObject) {
@ -134,29 +180,29 @@ class ASSerialWin: NSWindowController {
portHandle?.writeData(data) 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) { @IBAction func connect(AnyObject) {
shouldReconnect = false shouldReconnect = false
if portHandle != nil { if task != nil {
ASSerial.restorePort(portHandle!.fileDescriptor) task!.interrupt()
portHandle!.closeFile() } else if portHandle != nil {
let fd = portHandle!.fileDescriptor
ASSerial.restorePort(fd)
ASSerial.closePort(fd)
portHandle = nil portHandle = nil
} else { } else {
portHandle = ASSerial.openPort(port, withSpeed: Int32(baudRate)) portHandle = ASSerial.openPort(port, withSpeed: Int32(baudRate))
if portHandle != nil { installReader(portHandle)
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)
}
})
}
}
} }
} }
func disconnectTemporarily() { func disconnectTemporarily() {
@ -234,7 +280,7 @@ class ASSerialWin: NSWindowController {
func updatePortDefaults() { func updatePortDefaults() {
let userDefaults = NSUserDefaults.standardUserDefaults() let userDefaults = NSUserDefaults.standardUserDefaults()
let serialDefaults = NSMutableDictionary(dictionary:userDefaults.objectForKey("SerialDefaults") as NSDictionary) 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") userDefaults.setObject(serialDefaults, forKey:"SerialDefaults")
} }

View File

@ -13,20 +13,17 @@
</customObject> </customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5"> <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="751" height="403"/> <rect key="contentRect" x="196" y="240" width="751" height="403"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/> <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="se5-gp-TjO"> <view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="751" height="403"/> <rect key="frame" x="0.0" y="0.0" width="751" height="403"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rIR-2b-lAh"> <textField verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rIR-2b-lAh">
<rect key="frame" x="0.0" y="0.0" width="651" height="22"/> <rect key="frame" x="0.0" y="0.0" width="651" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="22" id="Y1V-fL-RCq"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="EBj-i4-UsA"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="EBj-i4-UsA">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -42,12 +39,12 @@
</connections> </connections>
</textField> </textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MOe-eO-fyb"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MOe-eO-fyb">
<rect key="frame" x="651" y="-1" width="50" height="22"/> <rect key="frame" x="651" y="0.0" width="50" height="20"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="20" id="eLx-yy-gCn"/> <constraint firstAttribute="height" constant="20" id="eLx-yy-gCn"/>
<constraint firstAttribute="width" constant="50" id="vaT-Kd-3pn"/> <constraint firstAttribute="width" constant="50" id="vaT-Kd-3pn"/>
</constraints> </constraints>
<buttonCell key="cell" type="smallSquare" title="Send" bezelStyle="smallSquare" imagePosition="overlaps" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Xt6-lT-b4z"> <buttonCell key="cell" type="square" title="Send" bezelStyle="shadowlessSquare" imagePosition="overlaps" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Xt6-lT-b4z">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
</buttonCell> </buttonCell>
@ -61,18 +58,15 @@
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="H05-Rp-UzR"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="H05-Rp-UzR">
<rect key="frame" x="701" y="-1" width="50" height="22"/> <rect key="frame" x="701" y="0.0" width="50" height="20"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="50" id="FWw-BQ-WKa"/> <constraint firstAttribute="width" constant="50" id="FWw-BQ-WKa"/>
<constraint firstAttribute="height" constant="20" id="s7i-ZT-S7W"/> <constraint firstAttribute="height" constant="20" id="s7i-ZT-S7W"/>
</constraints> </constraints>
<buttonCell key="cell" type="smallSquare" title="Now" bezelStyle="smallSquare" imagePosition="overlaps" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vZ4-fd-aE8"> <buttonCell key="cell" type="square" title="Now" bezelStyle="shadowlessSquare" imagePosition="overlaps" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vZ4-fd-aE8">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
</buttonCell> </buttonCell>
<connections>
<binding destination="-2" name="value" keyPath="scrollToBottom" id="xlw-TM-qoV"/>
</connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YB5-mV-vY7"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YB5-mV-vY7">
<rect key="frame" x="128" y="374" width="35" height="19"/> <rect key="frame" x="128" y="374" width="35" height="19"/>
@ -132,6 +126,11 @@
</popUpButtonCell> </popUpButtonCell>
<connections> <connections>
<binding destination="-2" name="selectedTag" keyPath="baudRate" id="pDO-CA-0VU"/> <binding destination="-2" name="selectedTag" keyPath="baudRate" id="pDO-CA-0VU"/>
<binding destination="-2" name="enabled" keyPath="task" id="plM-ZJ-GjO">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
</dictionary>
</binding>
</connections> </connections>
</popUpButton> </popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="r6K-Jm-mZe"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="r6K-Jm-mZe">
@ -191,6 +190,7 @@
</view> </view>
<connections> <connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/> <outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
<outlet property="initialFirstResponder" destination="rIR-2b-lAh" id="NJo-kw-Ewt"/>
</connections> </connections>
<point key="canvasLocation" x="271.5" y="416.5"/> <point key="canvasLocation" x="271.5" y="416.5"/>
</window> </window>

View File

@ -395,6 +395,11 @@
<action selector="uploadProject:" target="-1" id="Un4-q1-nWg"/> <action selector="uploadProject:" target="-1" id="Un4-q1-nWg"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Upload Terminal" keyEquivalent="U" id="jAF-od-ZFb">
<connections>
<action selector="uploadTerminal:" target="-1" id="hth-ZZ-S3I"/>
</connections>
</menuItem>
<menuItem title="Serial Monitor" id="GxC-cS-qvx"> <menuItem title="Serial Monitor" id="GxC-cS-qvx">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Serial Monitor" id="i2j-Ds-oRH"> <menu key="submenu" title="Serial Monitor" id="i2j-Ds-oRH">