Automatically connect / disconnect port

This commit is contained in:
Matthias Neeracher 2014-12-31 08:26:45 +01:00 committed by Matthias Neeracher
parent e47ce38ed1
commit 90f3d89376
7 changed files with 76 additions and 56 deletions

View File

@ -464,7 +464,13 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate {
@IBAction func uploadProject(sender: AnyObject) { @IBAction func uploadProject(sender: AnyObject) {
builder.continuation = { builder.continuation = {
self.selectNodeInOutline(self.files.uploadLog) self.selectNodeInOutline(self.files.uploadLog)
self.builder.uploadProject(self.board, programmer:self.programmer, port:self.port) ASSerialWin.portNeededForUpload(self.port)
self.builder.continuation = {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2*NSEC_PER_SEC)), dispatch_get_main_queue(), {
ASSerialWin.portAvailableAfterUpload(self.port)
})
}
self.builder.uploadProject(self.board, programmer:self.programmer, port:ASSerial.fileNameForPort(self.port))
} }
buildProject(sender) buildProject(sender)
} }

View File

@ -12,6 +12,7 @@ extern NSString * kASSerialPortsChanged;
@interface ASSerial : NSObject @interface ASSerial : NSObject
+ (NSString *) fileNameForPort:(NSString *)port;
+ (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;

View File

@ -38,10 +38,16 @@ NSString * kASSerialPortsChanged = @"PortsChanged";
return cuPorts; return cuPorts;
} }
+ (NSString *) fileNameForPort:(NSString *)port
{
if ([port containsString:@"/"])
return port;
else
return [NSString stringWithFormat:@"/dev/cu.%@", port];
}
+ (NSFileHandle *)openPort:(NSString *)port withSpeed:(int)speed { + (NSFileHandle *)openPort:(NSString *)port withSpeed:(int)speed {
if (![port containsString:@"/"]) int fd = open([[self fileNameForPort:port] UTF8String], O_RDWR | O_NOCTTY | O_NONBLOCK);
port = [NSString stringWithFormat:@"/dev/cu.%@", port];
int fd = open([port UTF8String], O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) if (fd < 0)
return nil; return nil;
if (ioctl(fd, TIOCEXCL) < 0) if (ioctl(fd, TIOCEXCL) < 0)

View File

@ -12,7 +12,6 @@ private var serialInstances = [String : ASSerialWin]()
private var keyboardHandler : ACEKeyboardHandler = .Ace private var keyboardHandler : ACEKeyboardHandler = .Ace
class ASSerialWin: NSWindowController { class ASSerialWin: NSWindowController {
@IBOutlet weak var portPopUp : NSPopUpButton!
@IBOutlet weak var inputLine : NSTextField! @IBOutlet weak var inputLine : NSTextField!
@IBOutlet weak var logView : ACEView! @IBOutlet weak var logView : ACEView!
@ -40,6 +39,7 @@ class ASSerialWin: NSWindowController {
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
class func showWindowWithPort(port: String) { class func showWindowWithPort(port: String) {
if let existing = serialInstances[port] { if let existing = serialInstances[port] {
@ -50,6 +50,16 @@ class ASSerialWin: NSWindowController {
newInstance.showWindow(self) newInstance.showWindow(self)
} }
} }
class func portNeededForUpload(port: String) {
if let existing = serialInstances[port] {
existing.disconnectTemporarily()
}
}
class func portAvailableAfterUpload(port: String) {
if let existing = serialInstances[port] {
existing.reconnect()
}
}
convenience init(port: String) { convenience init(port: String) {
self.init(windowNibName:"ASSerialWin") self.init(windowNibName:"ASSerialWin")
@ -80,7 +90,14 @@ class ASSerialWin: NSWindowController {
var nc = NSNotificationCenter.defaultCenter() var nc = NSNotificationCenter.defaultCenter()
serialObserver = nc.addObserverForName(kASSerialPortsChanged, object: nil, queue: nil, usingBlock: { (NSNotification) in serialObserver = nc.addObserverForName(kASSerialPortsChanged, object: nil, queue: nil, usingBlock: { (NSNotification) in
self.rebuildPortMenu() self.willChangeValueForKey("hasValidPort")
self.didChangeValueForKey("hasValidPort")
if self.hasValidPort {
self.reconnect()
} else {
self.disconnectTemporarily()
}
}) })
} }
@ -97,18 +114,11 @@ class ASSerialWin: NSWindowController {
logView.setFontSize(fontSize) logView.setFontSize(fontSize)
logView.setMode(UInt(ACEModeText)) logView.setMode(UInt(ACEModeText))
logView.alphaValue = 0.8 logView.alphaValue = 0.8
rebuildPortMenu()
window?.title = port window?.title = port
connect(self) connect(self)
super.windowDidLoad() super.windowDidLoad()
} }
func rebuildPortMenu() {
portPopUp.removeAllItems()
portPopUp.addItemsWithTitles(ASSerial.ports())
portPopUp.selectItemWithTitle(port)
}
@IBAction func selectPort(item: AnyObject) { @IBAction func selectPort(item: AnyObject) {
port = (item as NSPopUpButton).titleOfSelectedItem! port = (item as NSPopUpButton).titleOfSelectedItem!
window?.title = port window?.title = port
@ -121,6 +131,7 @@ class ASSerialWin: NSWindowController {
} }
@IBAction func connect(AnyObject) { @IBAction func connect(AnyObject) {
shouldReconnect = false
if portHandle != nil { if portHandle != nil {
ASSerial.restorePort(portHandle!.fileDescriptor) ASSerial.restorePort(portHandle!.fileDescriptor)
portHandle!.closeFile() portHandle!.closeFile()
@ -144,6 +155,17 @@ class ASSerialWin: NSWindowController {
} }
} }
} }
func disconnectTemporarily() {
if portHandle != nil {
connect(self) // Disconnect temporarily
shouldReconnect = true // But express interest to reconnect
}
}
func reconnect() {
if portHandle == nil && shouldReconnect {
connect(self) // Reconnect
}
}
var connectButtonTitle : String { var connectButtonTitle : String {
get { get {
@ -153,6 +175,11 @@ class ASSerialWin: NSWindowController {
class func keyPathsForValuesAffectingConnectButtonTitle() -> NSSet { class func keyPathsForValuesAffectingConnectButtonTitle() -> NSSet {
return NSSet(object: "portHandle") return NSSet(object: "portHandle")
} }
var hasValidPort : Bool {
get {
return (ASSerial.ports() as NSArray).containsObject(port)
}
}
// MARK: Editor configuration // MARK: Editor configuration

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C79" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C79" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
</dependencies> </dependencies>
@ -8,7 +8,6 @@
<connections> <connections>
<outlet property="inputLine" destination="rIR-2b-lAh" id="85R-3S-YGk"/> <outlet property="inputLine" destination="rIR-2b-lAh" id="85R-3S-YGk"/>
<outlet property="logView" destination="ta6-x2-NZt" id="0cN-zX-5mJ"/> <outlet property="logView" destination="ta6-x2-NZt" id="0cN-zX-5mJ"/>
<outlet property="portPopUp" destination="P3j-nY-jrB" id="6Gi-rp-uM4"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/> <outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections> </connections>
</customObject> </customObject>
@ -76,7 +75,7 @@
</connections> </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="336" y="372" width="35" height="19"/> <rect key="frame" x="128" y="374" width="35" height="19"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="35" id="M1a-Tj-gbw"/> <constraint firstAttribute="width" constant="35" id="M1a-Tj-gbw"/>
</constraints> </constraints>
@ -88,25 +87,8 @@
<binding destination="-2" name="value" keyPath="sendCR" id="HSR-Ct-mvX"/> <binding destination="-2" name="value" keyPath="sendCR" id="HSR-Ct-mvX"/>
</connections> </connections>
</button> </button>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P3j-nY-jrB">
<rect key="frame" x="20" y="369" width="200" height="25"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="Pcu-k9-a3F"/>
</constraints>
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="1gh-oB-cvv" id="dH3-sr-yK8">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="dld-b3-fF6">
<items>
<menuItem title="Item 1" state="on" id="1gh-oB-cvv"/>
<menuItem title="Item 2" id="dOl-ce-EBm"/>
<menuItem title="Item 3" id="md7-eV-5UJ"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="efl-ex-LQ4"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="efl-ex-LQ4">
<rect key="frame" x="228" y="369" width="100" height="25"/> <rect key="frame" x="20" y="371" width="100" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="100" id="aVR-Sh-ml8"/> <constraint firstAttribute="width" constant="100" id="aVR-Sh-ml8"/>
</constraints> </constraints>
@ -152,21 +134,8 @@
<binding destination="-2" name="selectedTag" keyPath="baudRate" id="pDO-CA-0VU"/> <binding destination="-2" name="selectedTag" keyPath="baudRate" id="pDO-CA-0VU"/>
</connections> </connections>
</popUpButton> </popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LrH-O2-kmq">
<rect key="frame" x="379" y="372" width="35" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="raR-vK-kQ1"/>
</constraints>
<buttonCell key="cell" type="recessed" title="LF" bezelStyle="recessed" alignment="center" state="on" borderStyle="border" inset="2" id="J4k-mA-Des">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
</buttonCell>
<connections>
<binding destination="-2" name="value" keyPath="sendLF" id="VbY-fJ-J9E"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="r6K-Jm-mZe"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="r6K-Jm-mZe">
<rect key="frame" x="651" y="369" width="80" height="25"/> <rect key="frame" x="651" y="371" width="80" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="80" id="0VI-Yv-M9O"/> <constraint firstAttribute="width" constant="80" id="0VI-Yv-M9O"/>
</constraints> </constraints>
@ -182,27 +151,38 @@
<customView translatesAutoresizingMaskIntoConstraints="NO" id="ta6-x2-NZt" customClass="ACEView"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="ta6-x2-NZt" customClass="ACEView">
<rect key="frame" x="0.0" y="20" width="751" height="343"/> <rect key="frame" x="0.0" y="20" width="751" height="343"/>
</customView> </customView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LrH-O2-kmq">
<rect key="frame" x="171" y="374" width="35" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="raR-vK-kQ1"/>
</constraints>
<buttonCell key="cell" type="recessed" title="LF" bezelStyle="recessed" alignment="center" state="on" borderStyle="border" inset="2" id="J4k-mA-Des">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
</buttonCell>
<connections>
<binding destination="-2" name="value" keyPath="sendLF" id="VbY-fJ-J9E"/>
</connections>
</button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="MOe-eO-fyb" firstAttribute="baseline" secondItem="H05-Rp-UzR" secondAttribute="baseline" id="1lu-Th-ZWy"/> <constraint firstItem="MOe-eO-fyb" firstAttribute="baseline" secondItem="H05-Rp-UzR" secondAttribute="baseline" id="1lu-Th-ZWy"/>
<constraint firstAttribute="bottom" secondItem="H05-Rp-UzR" secondAttribute="bottom" id="3yV-pC-gLd"/> <constraint firstAttribute="bottom" secondItem="H05-Rp-UzR" secondAttribute="bottom" id="3yV-pC-gLd"/>
<constraint firstItem="ta6-x2-NZt" firstAttribute="leading" secondItem="rIR-2b-lAh" secondAttribute="leading" id="3yl-1R-D3G"/> <constraint firstItem="ta6-x2-NZt" firstAttribute="leading" secondItem="rIR-2b-lAh" secondAttribute="leading" id="3yl-1R-D3G"/>
<constraint firstItem="LrH-O2-kmq" firstAttribute="centerY" secondItem="r6K-Jm-mZe" secondAttribute="centerY" id="6lX-PM-B49"/> <constraint firstItem="LrH-O2-kmq" firstAttribute="centerY" secondItem="r6K-Jm-mZe" secondAttribute="centerY" id="6lX-PM-B49"/>
<constraint firstItem="P3j-nY-jrB" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="77p-ZO-9Li"/>
<constraint firstItem="ta6-x2-NZt" firstAttribute="trailing" secondItem="H05-Rp-UzR" secondAttribute="trailing" id="8GP-Eq-sFq"/> <constraint firstItem="ta6-x2-NZt" firstAttribute="trailing" secondItem="H05-Rp-UzR" secondAttribute="trailing" id="8GP-Eq-sFq"/>
<constraint firstItem="efl-ex-LQ4" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="8" id="8ZQ-E8-8sv"/>
<constraint firstItem="r6K-Jm-mZe" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="LrH-O2-kmq" secondAttribute="trailing" constant="8" symbolic="YES" id="Fmj-AX-OLl"/> <constraint firstItem="r6K-Jm-mZe" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="LrH-O2-kmq" secondAttribute="trailing" constant="8" symbolic="YES" id="Fmj-AX-OLl"/>
<constraint firstItem="P3j-nY-jrB" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="10" id="R9e-ne-2Uq"/> <constraint firstItem="efl-ex-LQ4" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" id="Mb4-EJ-APg"/>
<constraint firstItem="LrH-O2-kmq" firstAttribute="leading" secondItem="YB5-mV-vY7" secondAttribute="trailing" constant="8" symbolic="YES" id="Txe-Zl-aYz"/> <constraint firstItem="LrH-O2-kmq" firstAttribute="leading" secondItem="YB5-mV-vY7" secondAttribute="trailing" constant="8" symbolic="YES" id="Txe-Zl-aYz"/>
<constraint firstItem="YB5-mV-vY7" firstAttribute="leading" secondItem="efl-ex-LQ4" secondAttribute="trailing" constant="8" symbolic="YES" id="Uc0-iA-Qbi"/> <constraint firstItem="YB5-mV-vY7" firstAttribute="leading" secondItem="efl-ex-LQ4" secondAttribute="trailing" constant="8" symbolic="YES" id="Uc0-iA-Qbi"/>
<constraint firstItem="efl-ex-LQ4" firstAttribute="baseline" secondItem="P3j-nY-jrB" secondAttribute="baseline" id="XLQ-85-9yK"/>
<constraint firstAttribute="trailing" secondItem="r6K-Jm-mZe" secondAttribute="trailing" constant="20" symbolic="YES" id="Y5v-lS-PaP"/> <constraint firstAttribute="trailing" secondItem="r6K-Jm-mZe" secondAttribute="trailing" constant="20" symbolic="YES" id="Y5v-lS-PaP"/>
<constraint firstItem="MOe-eO-fyb" firstAttribute="leading" secondItem="rIR-2b-lAh" secondAttribute="trailing" id="Zbk-LU-Ff1"/> <constraint firstItem="MOe-eO-fyb" firstAttribute="leading" secondItem="rIR-2b-lAh" secondAttribute="trailing" id="Zbk-LU-Ff1"/>
<constraint firstAttribute="bottom" secondItem="ta6-x2-NZt" secondAttribute="bottom" constant="20" symbolic="YES" id="bsR-t3-YF5"/> <constraint firstAttribute="bottom" secondItem="ta6-x2-NZt" secondAttribute="bottom" constant="20" symbolic="YES" id="bsR-t3-YF5"/>
<constraint firstItem="efl-ex-LQ4" firstAttribute="leading" secondItem="P3j-nY-jrB" secondAttribute="trailing" constant="8" symbolic="YES" id="cLW-5d-9Sf"/>
<constraint firstItem="H05-Rp-UzR" firstAttribute="leading" secondItem="MOe-eO-fyb" secondAttribute="trailing" id="cXy-nX-qgy"/> <constraint firstItem="H05-Rp-UzR" firstAttribute="leading" secondItem="MOe-eO-fyb" secondAttribute="trailing" id="cXy-nX-qgy"/>
<constraint firstAttribute="trailing" secondItem="H05-Rp-UzR" secondAttribute="trailing" id="dii-07-TA9"/> <constraint firstAttribute="trailing" secondItem="H05-Rp-UzR" secondAttribute="trailing" id="dii-07-TA9"/>
<constraint firstItem="ta6-x2-NZt" firstAttribute="top" secondItem="P3j-nY-jrB" secondAttribute="bottom" constant="8" symbolic="YES" id="f7E-bv-epu"/>
<constraint firstItem="rIR-2b-lAh" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" id="oK1-50-wsP"/> <constraint firstItem="rIR-2b-lAh" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" id="oK1-50-wsP"/>
<constraint firstItem="ta6-x2-NZt" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="40" id="r11-7Q-0Rf"/>
<constraint firstItem="LrH-O2-kmq" firstAttribute="baseline" secondItem="YB5-mV-vY7" secondAttribute="baseline" id="s4q-VN-G9l"/> <constraint firstItem="LrH-O2-kmq" firstAttribute="baseline" secondItem="YB5-mV-vY7" secondAttribute="baseline" id="s4q-VN-G9l"/>
<constraint firstItem="MOe-eO-fyb" firstAttribute="baseline" secondItem="rIR-2b-lAh" secondAttribute="baseline" id="t7L-ad-xpD"/> <constraint firstItem="MOe-eO-fyb" firstAttribute="baseline" secondItem="rIR-2b-lAh" secondAttribute="baseline" id="t7L-ad-xpD"/>
<constraint firstItem="H05-Rp-UzR" firstAttribute="top" secondItem="MOe-eO-fyb" secondAttribute="top" id="te2-kl-pfy"/> <constraint firstItem="H05-Rp-UzR" firstAttribute="top" secondItem="MOe-eO-fyb" secondAttribute="top" id="te2-kl-pfy"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7096" systemVersion="14C82" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C79" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7096"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="ASPreferences" customModule="AVRsack" customModuleProvider="target"> <customObject id="-2" userLabel="File's Owner" customClass="ASPreferences" customModule="AVRsack" customModuleProvider="target">

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C79" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C79" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
</dependencies> </dependencies>