В прошлом году я написал приложение под названием Disco Desktop, которое использует TinkerProxy для связи AIR с Arduino через последовательный порт USB.Мои классы TinkerProxy
и TinkerProxyEvent
размещены ниже.
Приложение связывает serproxy и использует API-интерфейс Native Process для его вызова, поэтому существует 2 различных установщика - Mac OS X и Windows.TinkerProxy.as
расширяет Socket, записывает файл конфигурации serproxy во время выполнения согласно вводу пользователя и запускает serproxy на основе этой конфигурации в качестве фонового процесса.Окно терминала / cmd никогда не отображается.
Примечание: вызов open()
вместо connect()
.В установщики включены простая схема устройства и эскиз для Arduino.
TinkerProxy
:
package com.mattie.net
{
//Imports
import com.mattie.events.TinkerProxyEvent;
import flash.desktop.NativeApplication;
import flash.desktop.NativeProcessStartupInfo;
import flash.desktop.NativeProcess;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.filesystem.File;
import flash.net.Socket;
import flash.system.Capabilities;
import flash.utils.Timer;
import flash.utils.Endian;
//Class
public class TinkerProxy extends Socket
{
//Properties
private var systemIsWindowsProperty:Boolean;
private var openingProperty:Boolean;
private var connectedProperty:Boolean;
//Variables
private var windowsProxyFile:String;
private var macProxyFile:String;
private var tinkerProxyApplication:File;
private var tinkerProxyConfigurationFile:File;
private var serialPort:String;
private var baudRate:uint;
private var networkAddress:String;
private var networkPort:uint;
private var loadDelay:uint;
private var loadDelayTimer:Timer;
private var initializeDelay:uint;
private var initializeDelayTimer:Timer;
private var comDatabits:uint;
private var comStopbits:uint;
private var proxyTimeout:uint;
private var writeConfigStream:FileStream;
private var tinkerProxyProcess:NativeProcess;
//Constructor
public function TinkerProxy(windowsProxyFile:String = "serproxy.exe", macProxyFile:String = "serproxy.osx", endian:String = Endian.LITTLE_ENDIAN)
{
//Set Included File Proxy Names
this.windowsProxyFile = windowsProxyFile;
this.macProxyFile = macProxyFile;
super();
super.endian = endian;
init();
}
//Resolve The Operating System
private function init():void
{
//Check If Source Tinker Proxy Files Are Included In Application Directory
if (!File.applicationDirectory.resolvePath(windowsProxyFile).exists && !File.applicationDirectory.resolvePath(macProxyFile).exists)
throw new Error("Tinker Proxy source files \"" + windowsProxyFile + "\" (Windows) and/or \"" + macProxyFile + "\" (Mac) cannot be found in application directory (Included Files)");
//Resoslve Operating System
if (Capabilities.os.toLowerCase().indexOf("windows") > -1)
{
systemIsWindowsProperty = true;
tinkerProxyApplication = File.applicationDirectory.resolvePath(windowsProxyFile);
tinkerProxyConfigurationFile = File.applicationStorageDirectory.resolvePath(windowsProxyFile.substring(0, windowsProxyFile.lastIndexOf(".exe")) + ".cfg");
}
else if (Capabilities.os.toLowerCase().indexOf("mac") > -1)
{
systemIsWindowsProperty = false;
tinkerProxyApplication = File.applicationDirectory.resolvePath(macProxyFile);
tinkerProxyConfigurationFile = File.applicationStorageDirectory.resolvePath(macProxyFile + ".cfg");
}
else
{
throw new Error("TinkerProxy Error: Operating System Is Not Supported");
}
}
//Open Tinker Proxy Socket Connection
public function open(
serialPort:String,
baudRate:uint,
networkAddress:String = "127.0.0.1",
networkPort:uint = 5331,
loadDelay:uint = 1000,
initializeDelay:uint = 2000,
comDatabits:uint = 8,
comStopbits:uint = 1,
proxyTimeout:uint = 63115200
)
{
//Disable Opening Socket If Currently Opening
if (!openingProperty)
{
//Set Accessor
openingProperty = true;
//Dispatch Event
dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.LOADING));
//Check If Connection Parameters For Configuration File Have Changed
if (
this.serialPort == serialPort &&
this.baudRate == baudRate &&
this.networkAddress == networkAddress &&
this.networkPort == networkPort &&
this.comDatabits == comDatabits &&
this.comStopbits == comStopbits &&
this.proxyTimeout == proxyTimeout
)
{
//Assign Timer Variables
this.loadDelay = loadDelay;
this.initializeDelay = initializeDelay;
//Launch Tinker Proxy Application If Connection Parameters Have Not Changed
launchTinkerProxyApplication(null);
return;
}
//Assign Variables
this.serialPort = serialPort;
this.baudRate = baudRate;
this.networkAddress = networkAddress;
this.networkPort = networkPort;
this.loadDelay = loadDelay;
this.initializeDelay = initializeDelay;
this.comDatabits = comDatabits;
this.comStopbits = comStopbits;
this.proxyTimeout = proxyTimeout;
//Add Event Listeners To New File Stream
writeConfigStream = new FileStream();
writeConfigStream.addEventListener(Event.CLOSE, launchTinkerProxyApplication);
writeConfigStream.addEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);
//Write Tinker Proxy Configuration File
writeConfigStream.openAsync(tinkerProxyConfigurationFile, FileMode.WRITE);
writeConfigStream.writeUTFBytes("serial_device1=" + serialPort + File.lineEnding);
writeConfigStream.writeUTFBytes("comm_ports=1" + File.lineEnding);
writeConfigStream.writeUTFBytes("net_port1=" + networkPort + File.lineEnding);
writeConfigStream.writeUTFBytes("newlines_to_nils=false" + File.lineEnding);
writeConfigStream.writeUTFBytes("comm_baud=" + baudRate + File.lineEnding);
writeConfigStream.writeUTFBytes("comm_databits=" + comDatabits + File.lineEnding);
writeConfigStream.writeUTFBytes("comm_stopbits=" + comStopbits+ File.lineEnding);
writeConfigStream.writeUTFBytes("comm_parity=none" + File.lineEnding);
writeConfigStream.writeUTFBytes("timeout=" + proxyTimeout + File.lineEnding);
writeConfigStream.close();
}
}
//Launch Tinker Proxy Application
private function launchTinkerProxyApplication(evt:Event):void
{
if (evt)
{
//Remove File Stream Event Listeners
writeConfigStream.removeEventListener(Event.CLOSE, launchTinkerProxyApplication);
writeConfigStream.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);
}
//Start Tinker Proxy Application As Native Process
var tinkerProxyProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
tinkerProxyProcessStartupInfo.executable = tinkerProxyApplication;
var processArguments:Vector.<String> = new Vector.<String>();
processArguments[0] = tinkerProxyConfigurationFile.nativePath;
tinkerProxyProcessStartupInfo.arguments = processArguments;
tinkerProxyProcess = new NativeProcess();
tinkerProxyProcess.start(tinkerProxyProcessStartupInfo);
//Delay Process To Allow Tinker Proxy Application To Initialize
loadDelayTimer = new Timer(loadDelay, 1);
loadDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
loadDelayTimer.start();
}
//Initialize Tinker Proxy Socket Connection
private function connectTinkerProxy(evt:TimerEvent):void
{
//Dispatch Event
dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.INITIALIZING));
//Remove Tinker Proxy Application Initilization Timer
loadDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
loadDelayTimer = null;
//Add Connection Error Event Listeners
addEventListener(Event.CONNECT, initializeDelayTimerHandler);
addEventListener(Event.CLOSE, connectionErrorEventHandler);
addEventListener(IOErrorEvent.IO_ERROR, connectionErrorEventHandler);
addEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionErrorEventHandler);
//Connect Socket (Super)
try {
super.connect(networkAddress, networkPort);
}
catch(error:IOError) {connectionErrorEventHandler(null);}
catch(error:SecurityError) {connectionErrorEventHandler(null);}
}
//Delay Process To Allow Device To Initialize
private function initializeDelayTimerHandler(evt:Event):void
{
removeEventListener(Event.CONNECT, initializeDelayTimerHandler);
initializeDelayTimer = new Timer(initializeDelay, 1);
initializeDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, tinkerProxyConnectionComplete);
initializeDelayTimer.start();
}
//Tinker Proxy Socket Has Been Successfully Connected
private function tinkerProxyConnectionComplete(evt:TimerEvent):void
{
//Set Accessors
openingProperty = false;
connectedProperty = true;
//Dispatch Event
dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.CONNECT));
//Remove Device Initilization Timer
initializeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, tinkerProxyConnectionComplete);
initializeDelayTimer = null;
}
//Throw Error If Stock Connect Method Is Explicitly Called
override public function connect(host:String, port:int):void
{
throw new Error("Cannot call connect() method on TinkerProxy instance. Call open() method instead.");
}
//Close Tinker Proxy Application
override public function close():void
{
//Stop Configuration File And Timers If Socket Is Currently Opening
if (openingProperty)
{
//Set Accessor
openingProperty = false;
//Stop File Stream
if (writeConfigStream.hasEventListener(Event.CLOSE))
{
writeConfigStream.close();
writeConfigStream.removeEventListener(Event.CLOSE, launchTinkerProxyApplication);
writeConfigStream.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);
}
//Stop Process Initialization Timer
if (loadDelayTimer.running)
{
loadDelayTimer.stop();
loadDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
loadDelayTimer = null;
}
//Stop Device Initialization Timer
if (initializeDelayTimer.running)
{
initializeDelayTimer.stop();
initializeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, connectTinkerProxy);
initializeDelayTimer = null;
}
}
//Close Socket (Super)
super.close();
//Close Tinker Proxy Application
tinkerProxyProcess.exit(true);
tinkerProxyProcess = null;
//Dispatch Event
dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.DISCONNECT));
//Set Accessor
connectedProperty = false;
//Remove Connection Error Event Listeners
removeEventListener(Event.CLOSE, connectionErrorEventHandler);
removeEventListener(IOErrorEvent.IO_ERROR, connectionErrorEventHandler);
removeEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionErrorEventHandler);
}
//Server Automatically Closed The Socket Due To A Connection Error
private function connectionErrorEventHandler(evt:*):void
{
//Set Accessors
openingProperty = false;
connectedProperty = false;
//Dispatch Event
dispatchEvent(new TinkerProxyEvent(TinkerProxyEvent.ERROR));
//Remove Device Initilization Timer
if (initializeDelayTimer != null)
{
if (initializeDelayTimer.running)
initializeDelayTimer.stop();
initializeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, tinkerProxyConnectionComplete);
initializeDelayTimer = null;
}
//Remove Connection Error Event Listeners
removeEventListener(Event.CLOSE, connectionErrorEventHandler);
removeEventListener(IOErrorEvent.IO_ERROR, connectionErrorEventHandler);
removeEventListener(SecurityErrorEvent.SECURITY_ERROR, connectionErrorEventHandler);
//Close Tinker Proxy Application
tinkerProxyProcess.exit(true);
tinkerProxyProcess = null;
}
//IO Error Event Handler
private function IOErrorEventHandler(evt:IOErrorEvent):void
{
throw new Error("TinkerProxy IOError: " + evt);
}
//Accessors
public function get systemIsWindows():Boolean
{
return systemIsWindowsProperty;
}
public function get opening():Boolean
{
return openingProperty;
}
override public function get connected():Boolean
{
return connectedProperty;
}
}
}
TinkerProxyEvent
:
package com.mattie.events
{
//Imports
import flash.events.Event;
//Class
public class TinkerProxyEvent extends Event
{
//Constants
public static const LOADING:String = "Loading";
public static const INITIALIZING:String = "Initializing";
public static const CONNECT:String = "Connect";
public static const DISCONNECT:String = "Disconnect";
public static const ERROR:String = "Error";
//Constructor
public function TinkerProxyEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
}
}
}