Subversion Repositories Projects

Compare Revisions

Ignore whitespace Rev 804 → Rev 805

/iKopter/trunk/Classes/Communication/AsyncSocket.h
0,0 → 1,511
//
// AsyncSocket.h
//
// This class is in the public domain.
// Originally created by Dustin Voss on Wed Jan 29 2003.
// Updated and maintained by Deusty Designs and the Mac development community.
//
// http://code.google.com/p/cocoaasyncsocket/
//
 
#import <Foundation/Foundation.h>
 
@class AsyncSocket;
@class AsyncReadPacket;
@class AsyncWritePacket;
 
extern NSString *const AsyncSocketException;
extern NSString *const AsyncSocketErrorDomain;
 
enum AsyncSocketError
{
AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum.
AsyncSocketNoError = 0, // Never used.
AsyncSocketCanceledError, // onSocketWillConnect: returned NO.
AsyncSocketConnectTimeoutError,
AsyncSocketReadMaxedOutError, // Reached set maxLength without completing
AsyncSocketReadTimeoutError,
AsyncSocketWriteTimeoutError
};
typedef enum AsyncSocketError AsyncSocketError;
 
@interface NSObject (AsyncSocketDelegate)
 
/**
* In the event of an error, the socket is closed.
* You may call "unreadData" during this call-back to get the last bit of data off the socket.
* When connecting, this delegate method may be called
* before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:".
**/
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
 
/**
* Called when a socket disconnects with or without error. If you want to release a socket after it disconnects,
* do so here. It is not safe to do that during "onSocket:willDisconnectWithError:".
*
* If you call the disconnect method, and the socket wasn't already disconnected,
* this delegate method will be called before the disconnect method returns.
**/
- (void)onSocketDidDisconnect:(AsyncSocket *)sock;
 
/**
* Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have
* the same delegate and will call "onSocket:didConnectToHost:port:".
**/
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
 
/**
* Called when a new socket is spawned to handle a connection. This method should return the run-loop of the
* thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used.
**/
- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket;
 
/**
* Called when a socket is about to connect. This method should return YES to continue, or NO to abort.
* If aborted, will result in AsyncSocketCanceledError.
*
* If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the
* CFReadStream and CFWriteStream as desired prior to connection.
*
* If the connectToAddress:error: method was called, the delegate will be able to access and configure the
* CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and
* configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method.
**/
- (BOOL)onSocketWillConnect:(AsyncSocket *)sock;
 
/**
* Called when a socket connects and is ready for reading and writing.
* The host parameter will be an IP address, not a DNS name.
**/
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
 
/**
* Called when a socket has completed reading the requested data into memory.
* Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
 
/**
* Called when a socket has read in data, but has not yet completed the read.
* This would occur if using readToData: or readToLength: methods.
* It may be used to for things such as updating progress bars.
**/
- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag;
 
/**
* Called when a socket has completed writing the requested data. Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
 
/**
* Called when a socket has written some data, but has not yet completed the entire write.
* It may be used to for things such as updating progress bars.
**/
- (void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(CFIndex)partialLength tag:(long)tag;
 
/**
* Called if a read operation has reached its timeout without completing.
* This method allows you to optionally extend the timeout.
* If you return a positive time interval (> 0) the read's timeout will be extended by the given amount.
* If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual.
*
* The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method.
* The length parameter is the number of bytes that have been read so far for the read operation.
*
* Note that this method may be called multiple times for a single read if you return positive numbers.
**/
- (NSTimeInterval)onSocket:(AsyncSocket *)sock
shouldTimeoutReadWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(CFIndex)length;
 
/**
* Called if a write operation has reached its timeout without completing.
* This method allows you to optionally extend the timeout.
* If you return a positive time interval (> 0) the write's timeout will be extended by the given amount.
* If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual.
*
* The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method.
* The length parameter is the number of bytes that have been written so far for the write operation.
*
* Note that this method may be called multiple times for a single write if you return positive numbers.
**/
- (NSTimeInterval)onSocket:(AsyncSocket *)sock
shouldTimeoutWriteWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(CFIndex)length;
 
/**
* Called after the socket has successfully completed SSL/TLS negotiation.
* This method is not called unless you use the provided startTLS method.
*
* If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close,
* and the onSocket:willDisconnectWithError: delegate method will be called with the specific SSL error code.
**/
- (void)onSocketDidSecure:(AsyncSocket *)sock;
 
@end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
@interface AsyncSocket : NSObject
{
CFSocketRef theSocket4; // IPv4 accept or connect socket
CFSocketRef theSocket6; // IPv6 accept or connect socket
CFReadStreamRef theReadStream;
CFWriteStreamRef theWriteStream;
 
CFRunLoopSourceRef theSource4; // For theSocket4
CFRunLoopSourceRef theSource6; // For theSocket6
CFRunLoopRef theRunLoop;
CFSocketContext theContext;
NSArray *theRunLoopModes;
NSTimer *theConnectTimer;
 
NSMutableArray *theReadQueue;
AsyncReadPacket *theCurrentRead;
NSTimer *theReadTimer;
NSMutableData *partialReadBuffer;
NSMutableArray *theWriteQueue;
AsyncWritePacket *theCurrentWrite;
NSTimer *theWriteTimer;
 
id theDelegate;
UInt16 theFlags;
long theUserData;
}
 
- (id)init;
- (id)initWithDelegate:(id)delegate;
- (id)initWithDelegate:(id)delegate userData:(long)userData;
 
/* String representation is long but has no "\n". */
- (NSString *)description;
 
/**
* Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate
* before changing it. It is, of course, safe to change the delegate before connecting or accepting connections.
**/
- (id)delegate;
- (BOOL)canSafelySetDelegate;
- (void)setDelegate:(id)delegate;
 
/* User data can be a long, or an id or void * cast to a long. */
- (long)userData;
- (void)setUserData:(long)userData;
 
/* Don't use these to read or write. And don't close them either! */
- (CFSocketRef)getCFSocket;
- (CFReadStreamRef)getCFReadStream;
- (CFWriteStreamRef)getCFWriteStream;
 
// Once one of the accept or connect methods are called, the AsyncSocket instance is locked in
// and the other accept/connect methods can't be called without disconnecting the socket first.
// If the attempt fails or times out, these methods either return NO or
// call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:".
 
// When an incoming connection is accepted, AsyncSocket invokes several delegate methods.
// These methods are (in chronological order):
// 1. onSocket:didAcceptNewSocket:
// 2. onSocket:wantsRunLoopForNewSocket:
// 3. onSocketWillConnect:
//
// Your server code will need to retain the accepted socket (if you want to accept it).
// The best place to do this is probably in the onSocket:didAcceptNewSocket: method.
//
// After the read and write streams have been setup for the newly accepted socket,
// the onSocket:didConnectToHost:port: method will be called on the proper run loop.
//
// Multithreading Note: If you're going to be moving the newly accepted socket to another run
// loop by implementing onSocket:wantsRunLoopForNewSocket:, then you should wait until the
// onSocket:didConnectToHost:port: method before calling read, write, or startTLS methods.
// Otherwise read/write events are scheduled on the incorrect runloop, and chaos may ensue.
 
/**
* Tells the socket to begin listening and accepting connections on the given port.
* When a connection comes in, the AsyncSocket instance will call the various delegate methods (see above).
* The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
**/
- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr;
 
/**
* This method is the same as acceptOnPort:error: with the additional option
* of specifying which interface to listen on. So, for example, if you were writing code for a server that
* has multiple IP addresses, you could specify which address you wanted to listen on. Or you could use it
* to specify that the socket should only accept connections over ethernet, and not other interfaces such as wifi.
* You may also use the special strings "localhost" or "loopback" to specify that
* the socket only accept connections from the local machine.
*
* To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method.
**/
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr;
 
/**
* Connects to the given host and port.
* The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2")
**/
- (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
 
/**
* This method is the same as connectToHost:onPort:error: with an additional timeout option.
* To not time out use a negative time interval, or simply use the connectToHost:onPort:error: method.
**/
- (BOOL)connectToHost:(NSString *)hostname
onPort:(UInt16)port
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr;
 
/**
* Connects to the given address, specified as a sockaddr structure wrapped in a NSData object.
* For example, a NSData object returned from NSNetservice's addresses method.
*
* If you have an existing struct sockaddr you can convert it to a NSData object like so:
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
 
/**
* This method is the same as connectToAddress:error: with an additional timeout option.
* To not time out use a negative time interval, or simply use the connectToAddress:error: method.
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
 
/**
* Disconnects immediately. Any pending reads or writes are dropped.
* If the socket is not already disconnected, the onSocketDidDisconnect delegate method
* will be called immediately, before this method returns.
*
* Please note the recommended way of releasing an AsyncSocket instance (e.g. in a dealloc method)
* [asyncSocket setDelegate:nil];
* [asyncSocket disconnect];
* [asyncSocket release];
**/
- (void)disconnect;
 
/**
* Disconnects after all pending reads have completed.
* After calling this, the read and write methods will do nothing.
* The socket will disconnect even if there are still pending writes.
**/
- (void)disconnectAfterReading;
 
/**
* Disconnects after all pending writes have completed.
* After calling this, the read and write methods will do nothing.
* The socket will disconnect even if there are still pending reads.
**/
- (void)disconnectAfterWriting;
 
/**
* Disconnects after all pending reads and writes have completed.
* After calling this, the read and write methods will do nothing.
**/
- (void)disconnectAfterReadingAndWriting;
 
/* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */
- (BOOL)isConnected;
 
/**
* Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected.
* The host will be an IP address.
**/
- (NSString *)connectedHost;
- (UInt16)connectedPort;
 
- (NSString *)localHost;
- (UInt16)localPort;
 
/**
* Returns the local or remote address to which this socket is connected,
* specified as a sockaddr structure wrapped in a NSData object.
*
* See also the connectedHost, connectedPort, localHost and localPort methods.
**/
- (NSData *)connectedAddress;
- (NSData *)localAddress;
 
/**
* Returns whether the socket is IPv4 or IPv6.
* An accepting socket may be both.
**/
- (BOOL)isIPv4;
- (BOOL)isIPv6;
 
// The readData and writeData methods won't block.
//
// You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.)
// If a read/write opertion times out, the corresponding "onSocket:shouldTimeout..." delegate method
// is called to optionally allow you to extend the timeout.
// Upon a timeout, the "onSocket:willDisconnectWithError:" method is called, followed by "onSocketDidDisconnect".
//
// The tag is for your convenience.
// You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data.
 
/**
* This will read a certain number of bytes into memory, and call the delegate method when those bytes have been read.
*
* If the length is 0, this method does nothing and the delegate is not called.
* If the timeout value is negative, the read operation will not use a timeout.
**/
- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
 
/**
* This reads bytes until (and including) the passed "data" parameter, which acts as a separator.
* The bytes and the separator are returned by the delegate method.
*
* If you pass nil or zero-length data as the "data" parameter,
* the method will do nothing, and the delegate will not be called.
* If the timeout value is negative, the read operation will not use a timeout.
*
* To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
* a character, the read will prematurely end.
**/
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
 
/**
* Same as readDataToData:withTimeout:tag, with the additional restriction that the amount of data read
* may not surpass the given maxLength (specified in bytes).
*
* If you pass a maxLength parameter that is less than the length of the data parameter,
* the method will do nothing, and the delegate will not be called.
*
* If the max length is surpassed, it is treated the same as a timeout - the socket is closed.
*
* Pass -1 as maxLength if no length restriction is desired, or simply use the readDataToData:withTimeout:tag method.
**/
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag;
 
/**
* Reads the first available bytes that become available on the socket.
*
* If the timeout value is negative, the read operation will not use a timeout.
**/
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
 
/**
* Writes data to the socket, and calls the delegate when finished.
*
* If you pass in nil or zero-length data, this method does nothing and the delegate will not be called.
* If the timeout value is negative, the write operation will not use a timeout.
**/
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
 
/**
* Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check).
* "tag", "done" and "total" will be filled in if they aren't NULL.
**/
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
 
/**
* Secures the connection using SSL/TLS.
*
* This method may be called at any time, and the TLS handshake will occur after all pending reads and writes
* are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing
* the upgrade to TLS at the same time, without having to wait for the write to finish.
* Any reads or writes scheduled after this method is called will occur over the secured connection.
*
* The possible keys and values for the TLS settings are well documented.
* Some possible keys are:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
*
* Please refer to Apple's documentation for associated values, as well as other possible keys.
*
* If you pass in nil or an empty dictionary, the default settings will be used.
*
* The default settings will check to make sure the remote party's certificate is signed by a
* trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired.
* However it will not verify the name on the certificate unless you
* give it a name to verify against via the kCFStreamSSLPeerName key.
* The security implications of this are important to understand.
* Imagine you are attempting to create a secure connection to MySecureServer.com,
* but your socket gets directed to MaliciousServer.com because of a hacked DNS server.
* If you simply use the default settings, and MaliciousServer.com has a valid certificate,
* the default settings will not detect any problems since the certificate is valid.
* To properly secure your connection in this particular scenario you
* should set the kCFStreamSSLPeerName property to "MySecureServer.com".
* If you do not know the peer name of the remote host in advance (for example, you're not sure
* if it will be "domain.com" or "www.domain.com"), then you can use the default settings to validate the
* certificate, and then use the X509Certificate class to verify the issuer after the socket has been secured.
* The X509Certificate class is part of the CocoaAsyncSocket open source project.
**/
- (void)startTLS:(NSDictionary *)tlsSettings;
 
/**
* For handling readDataToData requests, data is necessarily read from the socket in small increments.
* The performance can be much improved by allowing AsyncSocket to read larger chunks at a time and
* store any overflow in a small internal buffer.
* This is termed pre-buffering, as some data may be read for you before you ask for it.
* If you use readDataToData a lot, enabling pre-buffering will result in better performance, especially on the iPhone.
*
* The default pre-buffering state is controlled by the DEFAULT_PREBUFFERING definition.
* It is highly recommended one leave this set to YES.
*
* This method exists in case pre-buffering needs to be disabled by default for some unforeseen reason.
* In that case, this method exists to allow one to easily enable pre-buffering when ready.
**/
- (void)enablePreBuffering;
 
/**
* When you create an AsyncSocket, it is added to the runloop of the current thread.
* So for manually created sockets, it is easiest to simply create the socket on the thread you intend to use it.
*
* If a new socket is accepted, the delegate method onSocket:wantsRunLoopForNewSocket: is called to
* allow you to place the socket on a separate thread. This works best in conjunction with a thread pool design.
*
* If, however, you need to move the socket to a separate thread at a later time, this
* method may be used to accomplish the task.
*
* This method must be called from the thread/runloop the socket is currently running on.
*
* Note: After calling this method, all further method calls to this object should be done from the given runloop.
* Also, all delegate calls will be sent on the given runloop.
**/
- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
 
/**
* Allows you to configure which run loop modes the socket uses.
* The default set of run loop modes is NSDefaultRunLoopMode.
*
* If you'd like your socket to continue operation during other modes, you may want to add modes such as
* NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes.
*
* Accepted sockets will automatically inherit the same run loop modes as the listening socket.
*
* Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes.
**/
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
 
/**
* Returns the current run loop modes the AsyncSocket instance is operating in.
* The default set of run loop modes is NSDefaultRunLoopMode.
**/
- (NSArray *)runLoopModes;
 
/**
* In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
* any data that's left on the socket.
**/
- (NSData *)unreadData;
 
/* A few common line separators, for use with the readDataToData:... methods. */
+ (NSData *)CRLFData; // 0x0D0A
+ (NSData *)CRData; // 0x0D
+ (NSData *)LFData; // 0x0A
+ (NSData *)ZeroData; // 0x00
 
@end
/iKopter/trunk/Classes/Communication/AsyncSocket.m
0,0 → 1,3184
//
// AsyncSocket.m
//
// This class is in the public domain.
// Originally created by Dustin Voss on Wed Jan 29 2003.
// Updated and maintained by Deusty Designs and the Mac development community.
//
// http://code.google.com/p/cocoaasyncsocket/
//
 
#import "AsyncSocket.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <netdb.h>
 
#if TARGET_OS_IPHONE
// Note: You may need to add the CFNetwork Framework to your project
#import <CFNetwork/CFNetwork.h>
#endif
 
#pragma mark Declarations
 
#define DEFAULT_PREBUFFERING YES // Whether pre-buffering is enabled by default
 
#define READQUEUE_CAPACITY 5 // Initial capacity
#define WRITEQUEUE_CAPACITY 5 // Initial capacity
#define READALL_CHUNKSIZE 256 // Incremental increase in buffer size
#define WRITE_CHUNKSIZE (1024 * 4) // Limit on size of each write pass
 
NSString *const AsyncSocketException = @"AsyncSocketException";
NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain";
 
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
// Mutex lock used by all instances of AsyncSocket, to protect getaddrinfo.
// Prior to Mac OS X 10.5 this method was not thread-safe.
static NSString *getaddrinfoLock = @"lock";
#endif
 
enum AsyncSocketFlags
{
kEnablePreBuffering = 1 << 0, // If set, pre-buffering is enabled
kDidPassConnectMethod = 1 << 1, // If set, disconnection results in delegate call
kDidCompleteOpenForRead = 1 << 2, // If set, open callback has been called for read stream
kDidCompleteOpenForWrite = 1 << 3, // If set, open callback has been called for write stream
kStartingReadTLS = 1 << 4, // If set, we're waiting for TLS negotiation to complete
kStartingWriteTLS = 1 << 5, // If set, we're waiting for TLS negotiation to complete
kForbidReadsWrites = 1 << 6, // If set, no new reads or writes are allowed
kDisconnectAfterReads = 1 << 7, // If set, disconnect after no more reads are queued
kDisconnectAfterWrites = 1 << 8, // If set, disconnect after no more writes are queued
kClosingWithError = 1 << 9, // If set, the socket is being closed due to an error
kDequeueReadScheduled = 1 << 10, // If set, a maybeDequeueRead operation is already scheduled
kDequeueWriteScheduled = 1 << 11, // If set, a maybeDequeueWrite operation is already scheduled
kSocketCanAcceptBytes = 1 << 12, // If set, we know socket can accept bytes. If unset, it's unknown.
kSocketHasBytesAvailable = 1 << 13, // If set, we know socket has bytes available. If unset, it's unknown.
};
 
@interface AsyncSocket (Private)
 
// Connecting
- (void)startConnectTimeout:(NSTimeInterval)timeout;
- (void)endConnectTimeout;
 
// Socket Implementation
- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr;
- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
- (BOOL)configureSocketAndReturnError:(NSError **)errPtr;
- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
- (void)doAcceptWithSocket:(CFSocketNativeHandle)newSocket;
- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err;
 
// Stream Implementation
- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr;
- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr;
- (BOOL)openStreamsAndReturnError:(NSError **)errPtr;
- (void)doStreamOpen;
- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr;
 
// Disconnect Implementation
- (void)closeWithError:(NSError *)err;
- (void)recoverUnreadData;
- (void)emptyQueues;
- (void)close;
 
// Errors
- (NSError *)getErrnoError;
- (NSError *)getAbortError;
- (NSError *)getStreamError;
- (NSError *)getSocketError;
- (NSError *)getConnectTimeoutError;
- (NSError *)getReadMaxedOutError;
- (NSError *)getReadTimeoutError;
- (NSError *)getWriteTimeoutError;
- (NSError *)errorFromCFStreamError:(CFStreamError)err;
 
// Diagnostics
- (BOOL)isSocketConnected;
- (BOOL)areStreamsConnected;
- (NSString *)connectedHost:(CFSocketRef)socket;
- (UInt16)connectedPort:(CFSocketRef)socket;
- (NSString *)localHost:(CFSocketRef)socket;
- (UInt16)localPort:(CFSocketRef)socket;
- (NSString *)addressHost:(CFDataRef)cfaddr;
- (UInt16)addressPort:(CFDataRef)cfaddr;
 
// Reading
- (void)doBytesAvailable;
- (void)completeCurrentRead;
- (void)endCurrentRead;
- (void)scheduleDequeueRead;
- (void)maybeDequeueRead;
- (void)doReadTimeout:(NSTimer *)timer;
 
// Writing
- (void)doSendBytes;
- (void)completeCurrentWrite;
- (void)endCurrentWrite;
- (void)scheduleDequeueWrite;
- (void)maybeDequeueWrite;
- (void)maybeScheduleDisconnect;
- (void)doWriteTimeout:(NSTimer *)timer;
 
// Run Loop
- (void)runLoopAddSource:(CFRunLoopSourceRef)source;
- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source;
- (void)runLoopAddTimer:(NSTimer *)timer;
- (void)runLoopRemoveTimer:(NSTimer *)timer;
- (void)runLoopUnscheduleReadStream;
- (void)runLoopUnscheduleWriteStream;
 
// Security
- (void)maybeStartTLS;
- (void)onTLSHandshakeSuccessful;
 
// Callbacks
- (void)doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData;
- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream;
- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream;
 
@end
 
static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
static void MyCFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo);
static void MyCFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *pInfo);
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* The AsyncReadPacket encompasses the instructions for any given read.
* The content of a read packet allows the code to determine if we're:
* - reading to a certain length
* - reading to a certain separator
* - or simply reading the first chunk of available data
**/
@interface AsyncReadPacket : NSObject
{
@public
NSMutableData *buffer;
CFIndex bytesDone;
NSTimeInterval timeout;
CFIndex maxLength;
long tag;
NSData *term;
BOOL readAllAvailableData;
}
- (id)initWithData:(NSMutableData *)d
timeout:(NSTimeInterval)t
tag:(long)i
readAllAvailable:(BOOL)a
terminator:(NSData *)e
maxLength:(CFIndex)m;
 
- (unsigned)readLengthForTerm;
 
- (unsigned)prebufferReadLengthForTerm;
- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes;
@end
 
@implementation AsyncReadPacket
 
- (id)initWithData:(NSMutableData *)d
timeout:(NSTimeInterval)t
tag:(long)i
readAllAvailable:(BOOL)a
terminator:(NSData *)e
maxLength:(CFIndex)m
{
if((self = [super init]))
{
buffer = [d retain];
timeout = t;
tag = i;
readAllAvailableData = a;
term = [e copy];
bytesDone = 0;
maxLength = m;
}
return self;
}
 
/**
* For read packets with a set terminator, returns the safe length of data that can be read
* without going over a terminator, or the maxLength.
*
* It is assumed the terminator has not already been read.
**/
- (unsigned)readLengthForTerm
{
NSAssert(term != nil, @"Searching for term in data when there is no term.");
// What we're going to do is look for a partial sequence of the terminator at the end of the buffer.
// If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term,
// and we can only read that amount.
// Otherwise, we're safe to read the entire length of the term.
unsigned result = [term length];
// Shortcut when term is a single byte
if(result == 1) return result;
// i = index within buffer at which to check data
// j = length of term to check against
// Note: Beware of implicit casting rules
// This could give you -1: MAX(0, (0 - [term length] + 1));
CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1));
CFIndex j = MIN([term length] - 1, bytesDone);
while(i < bytesDone)
{
const void *subBuffer = [buffer bytes] + i;
if(memcmp(subBuffer, [term bytes], j) == 0)
{
result = [term length] - j;
break;
}
i++;
j--;
}
if(maxLength > 0)
return MIN(result, (maxLength - bytesDone));
else
return result;
}
 
/**
* Assuming pre-buffering is enabled, returns the amount of data that can be read
* without going over the maxLength.
**/
- (unsigned)prebufferReadLengthForTerm
{
if(maxLength > 0)
return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone));
else
return READALL_CHUNKSIZE;
}
 
/**
* For read packets with a set terminator, scans the packet buffer for the term.
* It is assumed the terminator had not been fully read prior to the new bytes.
*
* If the term is found, the number of excess bytes after the term are returned.
* If the term is not found, this method will return -1.
*
* Note: A return value of zero means the term was found at the very end.
**/
- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes
{
NSAssert(term != nil, @"Searching for term in data when there is no term.");
// We try to start the search such that the first new byte read matches up with the last byte of the term.
// We continue searching forward after this until the term no longer fits into the buffer.
// Note: Beware of implicit casting rules
// This could give you -1: MAX(0, 1 - 1 - [term length] + 1);
CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1));
while(i + [term length] <= bytesDone)
{
const void *subBuffer = [buffer bytes] + i;
if(memcmp(subBuffer, [term bytes], [term length]) == 0)
{
return bytesDone - (i + [term length]);
}
i++;
}
return -1;
}
 
- (void)dealloc
{
[buffer release];
[term release];
[super dealloc];
}
 
@end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* The AsyncWritePacket encompasses the instructions for any given write.
**/
@interface AsyncWritePacket : NSObject
{
@public
NSData *buffer;
CFIndex bytesDone;
long tag;
NSTimeInterval timeout;
}
- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i;
@end
 
@implementation AsyncWritePacket
 
- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
{
if((self = [super init]))
{
buffer = [d retain];
timeout = t;
tag = i;
bytesDone = 0;
}
return self;
}
 
- (void)dealloc
{
[buffer release];
[super dealloc];
}
 
@end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* The AsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues.
* This class my be altered to support more than just TLS in the future.
**/
@interface AsyncSpecialPacket : NSObject
{
@public
NSDictionary *tlsSettings;
}
- (id)initWithTLSSettings:(NSDictionary *)settings;
@end
 
@implementation AsyncSpecialPacket
 
- (id)initWithTLSSettings:(NSDictionary *)settings
{
if((self = [super init]))
{
tlsSettings = [settings copy];
}
return self;
}
 
- (void)dealloc
{
[tlsSettings release];
[super dealloc];
}
 
@end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
@implementation AsyncSocket
 
- (id)init
{
return [self initWithDelegate:nil userData:0];
}
 
- (id)initWithDelegate:(id)delegate
{
return [self initWithDelegate:delegate userData:0];
}
 
// Designated initializer.
- (id)initWithDelegate:(id)delegate userData:(long)userData
{
if((self = [super init]))
{
theFlags = DEFAULT_PREBUFFERING ? kEnablePreBuffering : 0;
theDelegate = delegate;
theUserData = userData;
theSocket4 = NULL;
theSource4 = NULL;
theSocket6 = NULL;
theSource6 = NULL;
theRunLoop = NULL;
theReadStream = NULL;
theWriteStream = NULL;
theConnectTimer = nil;
theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY];
theCurrentRead = nil;
theReadTimer = nil;
partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE];
theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY];
theCurrentWrite = nil;
theWriteTimer = nil;
// Socket context
NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext");
theContext.version = 0;
theContext.info = self;
theContext.retain = nil;
theContext.release = nil;
theContext.copyDescription = nil;
// Default run loop modes
theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain];
}
return self;
}
 
// The socket may been initialized in a connected state and auto-released, so this should close it down cleanly.
- (void)dealloc
{
[self close];
[theReadQueue release];
[theWriteQueue release];
[theRunLoopModes release];
[partialReadBuffer release];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[super dealloc];
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Accessors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (long)userData
{
return theUserData;
}
 
- (void)setUserData:(long)userData
{
theUserData = userData;
}
 
- (id)delegate
{
return theDelegate;
}
 
- (void)setDelegate:(id)delegate
{
theDelegate = delegate;
}
 
- (BOOL)canSafelySetDelegate
{
return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil);
}
 
- (CFSocketRef)getCFSocket
{
if(theSocket4)
return theSocket4;
else
return theSocket6;
}
 
- (CFReadStreamRef)getCFReadStream
{
return theReadStream;
}
 
- (CFWriteStreamRef)getCFWriteStream
{
return theWriteStream;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Progress
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
{
// Check to make sure we're actually reading something right now,
// and that the read packet isn't an AsyncSpecialPacket (upgrade to TLS).
if (!theCurrentRead || ![theCurrentRead isKindOfClass:[AsyncReadPacket class]]) return NAN;
// It's only possible to know the progress of our read if we're reading to a certain length
// If we're reading to data, we of course have no idea when the data will arrive
// If we're reading to timeout, then we have no idea when the next chunk of data will arrive.
BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil);
CFIndex d = theCurrentRead->bytesDone;
CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0;
if (tag != NULL) *tag = theCurrentRead->tag;
if (done != NULL) *done = d;
if (total != NULL) *total = t;
float ratio = (float)d/(float)t;
return isnan(ratio) ? 1.0F : ratio; // 0 of 0 bytes is 100% done.
}
 
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
{
// Check to make sure we're actually writing something right now,
// and that the write packet isn't an AsyncSpecialPacket (upgrade to TLS).
if (!theCurrentWrite || ![theCurrentWrite isKindOfClass:[AsyncWritePacket class]]) return NAN;
CFIndex d = theCurrentWrite->bytesDone;
CFIndex t = [theCurrentWrite->buffer length];
if (tag != NULL) *tag = theCurrentWrite->tag;
if (done != NULL) *done = d;
if (total != NULL) *total = t;
return (float)d/(float)t;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Run Loop
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (void)runLoopAddSource:(CFRunLoopSourceRef)source
{
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFRunLoopAddSource(theRunLoop, source, runLoopMode);
}
}
 
- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source
{
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFRunLoopRemoveSource(theRunLoop, source, runLoopMode);
}
}
 
- (void)runLoopAddTimer:(NSTimer *)timer
{
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
}
}
 
- (void)runLoopRemoveTimer:(NSTimer *)timer
{
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
}
}
 
- (void)runLoopUnscheduleReadStream
{
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
}
CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
}
 
- (void)runLoopUnscheduleWriteStream
{
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
}
CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
}
 
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Configuration
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* See the header file for a full explanation of pre-buffering.
**/
- (void)enablePreBuffering
{
theFlags |= kEnablePreBuffering;
}
 
/**
* See the header file for a full explanation of this method.
**/
- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
{
NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
@"moveToRunLoop must be called from within the current RunLoop!");
if(runLoop == nil)
{
return NO;
}
if(theRunLoop == [runLoop getCFRunLoop])
{
return YES;
}
[NSObject cancelPreviousPerformRequestsWithTarget:self];
theFlags &= ~kDequeueReadScheduled;
theFlags &= ~kDequeueWriteScheduled;
if(theReadStream && theWriteStream)
{
[self runLoopUnscheduleReadStream];
[self runLoopUnscheduleWriteStream];
}
if(theSource4) [self runLoopRemoveSource:theSource4];
if(theSource6) [self runLoopRemoveSource:theSource6];
// We do not retain the timers - they get retained by the runloop when we add them as a source.
// Since we're about to remove them as a source, we retain now, and release again below.
[theReadTimer retain];
[theWriteTimer retain];
if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
theRunLoop = [runLoop getCFRunLoop];
if(theReadTimer) [self runLoopAddTimer:theReadTimer];
if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
// Release timers since we retained them above
[theReadTimer release];
[theWriteTimer release];
if(theSource4) [self runLoopAddSource:theSource4];
if(theSource6) [self runLoopAddSource:theSource6];
if(theReadStream && theWriteStream)
{
if(![self attachStreamsToRunLoop:runLoop error:nil])
{
return NO;
}
}
[runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
[runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes];
[runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes];
return YES;
}
 
/**
* See the header file for a full explanation of this method.
**/
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes
{
NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
@"setRunLoopModes must be called from within the current RunLoop!");
if([runLoopModes count] == 0)
{
return NO;
}
if([theRunLoopModes isEqualToArray:runLoopModes])
{
return YES;
}
[NSObject cancelPreviousPerformRequestsWithTarget:self];
theFlags &= ~kDequeueReadScheduled;
theFlags &= ~kDequeueWriteScheduled;
if(theReadStream && theWriteStream)
{
[self runLoopUnscheduleReadStream];
[self runLoopUnscheduleWriteStream];
}
if(theSource4) [self runLoopRemoveSource:theSource4];
if(theSource6) [self runLoopRemoveSource:theSource6];
// We do not retain the timers - they get retained by the runloop when we add them as a source.
// Since we're about to remove them as a source, we retain now, and release again below.
[theReadTimer retain];
[theWriteTimer retain];
if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
[theRunLoopModes release];
theRunLoopModes = [runLoopModes copy];
if(theReadTimer) [self runLoopAddTimer:theReadTimer];
if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
// Release timers since we retained them above
[theReadTimer release];
[theWriteTimer release];
if(theSource4) [self runLoopAddSource:theSource4];
if(theSource6) [self runLoopAddSource:theSource6];
if(theReadStream && theWriteStream)
{
// Note: theRunLoop variable is a CFRunLoop, and NSRunLoop is NOT toll-free bridged with CFRunLoop.
// So we cannot pass theRunLoop to the method below, which is expecting a NSRunLoop parameter.
// Instead we pass nil, which will result in the method properly using the current run loop.
if(![self attachStreamsToRunLoop:nil error:nil])
{
return NO;
}
}
[self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
[self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
[self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
return YES;
}
 
- (NSArray *)runLoopModes
{
return [[theRunLoopModes retain] autorelease];
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Accepting
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr
{
return [self acceptOnInterface:nil port:port error:errPtr];
}
/**
* To accept on a certain interface, pass the address to accept on.
* To accept on any interface, pass nil or an empty string.
* To accept only connections from localhost pass "localhost" or "loopback".
**/
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr
{
if (theDelegate == NULL)
{
[NSException raise:AsyncSocketException
format:@"Attempting to accept without a delegate. Set a delegate first."];
}
if (theSocket4 != NULL || theSocket6 != NULL)
{
[NSException raise:AsyncSocketException
format:@"Attempting to accept while connected or accepting connections. Disconnect first."];
}
 
// Set up the listen sockaddr structs if needed.
NSData *address4 = nil, *address6 = nil;
if(interface == nil || ([interface length] == 0))
{
// Accept on ANY address
struct sockaddr_in nativeAddr4;
nativeAddr4.sin_len = sizeof(struct sockaddr_in);
nativeAddr4.sin_family = AF_INET;
nativeAddr4.sin_port = htons(port);
nativeAddr4.sin_addr.s_addr = htonl(INADDR_ANY);
memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
struct sockaddr_in6 nativeAddr6;
nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
nativeAddr6.sin6_family = AF_INET6;
nativeAddr6.sin6_port = htons(port);
nativeAddr6.sin6_flowinfo = 0;
nativeAddr6.sin6_addr = in6addr_any;
nativeAddr6.sin6_scope_id = 0;
// Wrap the native address structures for CFSocketSetAddress.
address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
}
else if([interface isEqualToString:@"localhost"] || [interface isEqualToString:@"loopback"])
{
// Accept only on LOOPBACK address
struct sockaddr_in nativeAddr4;
nativeAddr4.sin_len = sizeof(struct sockaddr_in);
nativeAddr4.sin_family = AF_INET;
nativeAddr4.sin_port = htons(port);
nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
struct sockaddr_in6 nativeAddr6;
nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
nativeAddr6.sin6_family = AF_INET6;
nativeAddr6.sin6_port = htons(port);
nativeAddr6.sin6_flowinfo = 0;
nativeAddr6.sin6_addr = in6addr_loopback;
nativeAddr6.sin6_scope_id = 0;
// Wrap the native address structures for CFSocketSetAddress.
address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
}
else
{
NSString *portStr = [NSString stringWithFormat:@"%hu", port];
 
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
@synchronized (getaddrinfoLock)
#endif
{
struct addrinfo hints, *res, *res0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
int error = getaddrinfo([interface UTF8String], [portStr UTF8String], &hints, &res0);
if(error)
{
if(errPtr)
{
NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
*errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
}
}
for(res = res0; res; res = res->ai_next)
{
if(!address4 && (res->ai_family == AF_INET))
{
// Found IPv4 address
// Wrap the native address structures for CFSocketSetAddress.
address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
}
else if(!address6 && (res->ai_family == AF_INET6))
{
// Found IPv6 address
// Wrap the native address structures for CFSocketSetAddress.
address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
}
}
freeaddrinfo(res0);
}
if(!address4 && !address6) return NO;
}
 
// Create the sockets.
 
if (address4)
{
theSocket4 = [self newAcceptSocketForAddress:address4 error:errPtr];
if (theSocket4 == NULL) goto Failed;
}
if (address6)
{
theSocket6 = [self newAcceptSocketForAddress:address6 error:errPtr];
// Note: The iPhone doesn't currently support IPv6
#if !TARGET_OS_IPHONE
if (theSocket6 == NULL) goto Failed;
#endif
}
// Attach the sockets to the run loop so that callback methods work
[self attachSocketsToRunLoop:nil error:nil];
// Set the SO_REUSEADDR flags.
 
int reuseOn = 1;
if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
 
// Set the local bindings which causes the sockets to start listening.
 
CFSocketError err;
if (theSocket4)
{
err = CFSocketSetAddress (theSocket4, (CFDataRef)address4);
if (err != kCFSocketSuccess) goto Failed;
//NSLog(@"theSocket4: %hu", [self localPort:theSocket4]);
}
if(port == 0 && theSocket4 && theSocket6)
{
// The user has passed in port 0, which means he wants to allow the kernel to choose the port for them
// However, the kernel will choose a different port for both theSocket4 and theSocket6
// So we grab the port the kernel choose for theSocket4, and set it as the port for theSocket6
UInt16 chosenPort = [self localPort:theSocket4];
struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
pSockAddr6->sin6_port = htons(chosenPort);
}
if (theSocket6)
{
err = CFSocketSetAddress (theSocket6, (CFDataRef)address6);
if (err != kCFSocketSuccess) goto Failed;
//NSLog(@"theSocket6: %hu", [self localPort:theSocket6]);
}
 
theFlags |= kDidPassConnectMethod;
return YES;
Failed:
if(errPtr) *errPtr = [self getSocketError];
if(theSocket4 != NULL)
{
CFSocketInvalidate(theSocket4);
CFRelease(theSocket4);
theSocket4 = NULL;
}
if(theSocket6 != NULL)
{
CFSocketInvalidate(theSocket6);
CFRelease(theSocket6);
theSocket6 = NULL;
}
return NO;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Connecting
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr
{
return [self connectToHost:hostname onPort:port withTimeout:-1 error:errPtr];
}
 
/**
* This method creates an initial CFReadStream and CFWriteStream to the given host on the given port.
* The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds.
*
* Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection,
* specifically in the onSocketWillConnect: method.
**/
- (BOOL)connectToHost:(NSString *)hostname
onPort:(UInt16)port
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr
{
if(theDelegate == NULL)
{
[NSException raise:AsyncSocketException
format:@"Attempting to connect without a delegate. Set a delegate first."];
}
 
if(theSocket4 != NULL || theSocket6 != NULL)
{
[NSException raise:AsyncSocketException
format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
}
if(![self createStreamsToHost:hostname onPort:port error:errPtr]) goto Failed;
if(![self attachStreamsToRunLoop:nil error:errPtr]) goto Failed;
if(![self configureStreamsAndReturnError:errPtr]) goto Failed;
if(![self openStreamsAndReturnError:errPtr]) goto Failed;
[self startConnectTimeout:timeout];
theFlags |= kDidPassConnectMethod;
return YES;
Failed:
[self close];
return NO;
}
 
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
{
return [self connectToAddress:remoteAddr withTimeout:-1 error:errPtr];
}
 
/**
* This method creates an initial CFSocket to the given address.
* The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be
* created from the low-level sockets after the connection succeeds.
*
* Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection,
* specifically in the onSocketWillConnect: method.
*
* Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from
* NSNetservice addresses method.
* If you have an existing struct sockaddr you can convert it to an NSData object like so:
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
{
if (theDelegate == NULL)
{
[NSException raise:AsyncSocketException
format:@"Attempting to connect without a delegate. Set a delegate first."];
}
if (theSocket4 != NULL || theSocket6 != NULL)
{
[NSException raise:AsyncSocketException
format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
}
if(![self createSocketForAddress:remoteAddr error:errPtr]) goto Failed;
if(![self attachSocketsToRunLoop:nil error:errPtr]) goto Failed;
if(![self configureSocketAndReturnError:errPtr]) goto Failed;
if(![self connectSocketToAddress:remoteAddr error:errPtr]) goto Failed;
[self startConnectTimeout:timeout];
theFlags |= kDidPassConnectMethod;
return YES;
Failed:
[self close];
return NO;
}
 
- (void)startConnectTimeout:(NSTimeInterval)timeout
{
if(timeout >= 0.0)
{
theConnectTimer = [NSTimer timerWithTimeInterval:timeout
target:self
selector:@selector(doConnectTimeout:)
userInfo:nil
repeats:NO];
[self runLoopAddTimer:theConnectTimer];
}
}
 
- (void)endConnectTimeout
{
[theConnectTimer invalidate];
theConnectTimer = nil;
}
 
- (void)doConnectTimeout:(NSTimer *)timer
{
[self endConnectTimeout];
[self closeWithError:[self getConnectTimeoutError]];
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Socket Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* Creates the accept sockets.
* Returns true if either IPv4 or IPv6 is created.
* If either is missing, an error is returned (even though the method may return true).
**/
- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr
{
struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes];
int addressFamily = pSockAddr->sa_family;
CFSocketRef theSocket = CFSocketCreate(kCFAllocatorDefault,
addressFamily,
SOCK_STREAM,
0,
kCFSocketAcceptCallBack, // Callback flags
(CFSocketCallBack)&MyCFSocketCallback, // Callback method
&theContext);
 
if(theSocket == NULL)
{
if(errPtr) *errPtr = [self getSocketError];
}
return theSocket;
}
 
- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr
{
struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes];
if(pSockAddr->sa_family == AF_INET)
{
theSocket4 = CFSocketCreate(NULL, // Default allocator
PF_INET, // Protocol Family
SOCK_STREAM, // Socket Type
IPPROTO_TCP, // Protocol
kCFSocketConnectCallBack, // Callback flags
(CFSocketCallBack)&MyCFSocketCallback, // Callback method
&theContext); // Socket Context
if(theSocket4 == NULL)
{
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
}
else if(pSockAddr->sa_family == AF_INET6)
{
theSocket6 = CFSocketCreate(NULL, // Default allocator
PF_INET6, // Protocol Family
SOCK_STREAM, // Socket Type
IPPROTO_TCP, // Protocol
kCFSocketConnectCallBack, // Callback flags
(CFSocketCallBack)&MyCFSocketCallback, // Callback method
&theContext); // Socket Context
if(theSocket6 == NULL)
{
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
}
else
{
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
return YES;
}
 
/**
* Adds the CFSocket's to the run-loop so that callbacks will work properly.
**/
- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
{
// Get the CFRunLoop to which the socket should be attached.
theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
if(theSocket4)
{
theSource4 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket4, 0);
[self runLoopAddSource:theSource4];
}
if(theSocket6)
{
theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0);
[self runLoopAddSource:theSource6];
}
return YES;
}
 
/**
* Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect.
* Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened.
**/
- (BOOL)configureSocketAndReturnError:(NSError **)errPtr
{
// Call the delegate method for further configuration.
if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
{
if([theDelegate onSocketWillConnect:self] == NO)
{
if (errPtr) *errPtr = [self getAbortError];
return NO;
}
}
return YES;
}
 
- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
{
// Start connecting to the given address in the background
// The MyCFSocketCallback method will be called when the connection succeeds or fails
if(theSocket4)
{
CFSocketError err = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, -1);
if(err != kCFSocketSuccess)
{
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
}
else if(theSocket6)
{
CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1);
if(err != kCFSocketSuccess)
{
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
}
return YES;
}
 
/**
* Attempt to make the new socket.
* If an error occurs, ignore this event.
**/
- (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative
{
// New socket inherits same delegate and run loop modes.
// Note: We use [self class] to support subclassing AsyncSocket.
AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease];
[newSocket setRunLoopModes:theRunLoopModes];
if(newSocket)
{
if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)])
[theDelegate onSocket:self didAcceptNewSocket:newSocket];
NSRunLoop *runLoop = nil;
if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)])
runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket];
BOOL pass = YES;
if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO;
if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil]) pass = NO;
if(pass && ![newSocket configureStreamsAndReturnError:nil]) pass = NO;
if(pass && ![newSocket openStreamsAndReturnError:nil]) pass = NO;
if(pass)
newSocket->theFlags |= kDidPassConnectMethod;
else {
// No NSError, but errors will still get logged from the above functions.
[newSocket close];
}
}
}
 
/**
* Description forthcoming...
**/
- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError
{
NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
if(socketError == kCFSocketTimeout || socketError == kCFSocketError)
{
[self closeWithError:[self getSocketError]];
return;
}
// Get the underlying native (BSD) socket
CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock);
// Setup the socket so that invalidating the socket will not close the native socket
CFSocketSetSocketFlags(sock, 0);
// Invalidate and release the CFSocket - All we need from here on out is the nativeSocket
// Note: If we don't invalidate the socket (leaving the native socket open)
// then theReadStream and theWriteStream won't function properly.
// Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted.
// I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created,
// as opposed to the CFReadStream/CFWriteStream.
CFSocketInvalidate(sock);
CFRelease(sock);
theSocket4 = NULL;
theSocket6 = NULL;
NSError *err;
BOOL pass = YES;
if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO;
if(pass && ![self attachStreamsToRunLoop:nil error:&err]) pass = NO;
if(pass && ![self openStreamsAndReturnError:&err]) pass = NO;
if(!pass)
{
[self closeWithError:err];
}
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Stream Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* Creates the CFReadStream and CFWriteStream from the given native socket.
* The CFSocket may be extracted from either stream after the streams have been opened.
*
* Note: The given native socket must already be connected!
**/
- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr
{
// Create the socket & streams.
CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream);
if (theReadStream == NULL || theWriteStream == NULL)
{
NSError *err = [self getStreamError];
NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err);
if (errPtr) *errPtr = err;
return NO;
}
// Ensure the CF & BSD socket is closed when the streams are closed.
CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
return YES;
}
 
/**
* Creates the CFReadStream and CFWriteStream from the given hostname and port number.
* The CFSocket may be extracted from either stream after the streams have been opened.
**/
- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr
{
// Create the socket & streams.
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream);
if (theReadStream == NULL || theWriteStream == NULL)
{
if (errPtr) *errPtr = [self getStreamError];
return NO;
}
// Ensure the CF & BSD socket is closed when the streams are closed.
CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
return YES;
}
 
- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
{
// Get the CFRunLoop to which the socket should be attached.
theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
 
// Setup read stream callbacks
CFOptionFlags readStreamEvents = kCFStreamEventHasBytesAvailable |
kCFStreamEventErrorOccurred |
kCFStreamEventEndEncountered |
kCFStreamEventOpenCompleted;
if (!CFReadStreamSetClient(theReadStream,
readStreamEvents,
(CFReadStreamClientCallBack)&MyCFReadStreamCallback,
(CFStreamClientContext *)(&theContext)))
{
NSError *err = [self getStreamError];
NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self);
NSLog (@"Error: %@", err);
if (errPtr) *errPtr = err;
return NO;
}
 
// Setup write stream callbacks
CFOptionFlags writeStreamEvents = kCFStreamEventCanAcceptBytes |
kCFStreamEventErrorOccurred |
kCFStreamEventEndEncountered |
kCFStreamEventOpenCompleted;
if (!CFWriteStreamSetClient (theWriteStream,
writeStreamEvents,
(CFWriteStreamClientCallBack)&MyCFWriteStreamCallback,
(CFStreamClientContext *)(&theContext)))
{
NSError *err = [self getStreamError];
NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self);
NSLog (@"Error: %@", err);
if (errPtr) *errPtr = err;
return NO;
}
// Add read and write streams to run loop
unsigned i, count = [theRunLoopModes count];
for(i = 0; i < count; i++)
{
CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
CFReadStreamScheduleWithRunLoop(theReadStream, theRunLoop, runLoopMode);
CFWriteStreamScheduleWithRunLoop(theWriteStream, theRunLoop, runLoopMode);
}
return YES;
}
 
/**
* Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect.
* Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened.
**/
- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr
{
// Call the delegate method for further configuration.
if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
{
if([theDelegate onSocketWillConnect:self] == NO)
{
if (errPtr) *errPtr = [self getAbortError];
return NO;
}
}
return YES;
}
 
- (BOOL)openStreamsAndReturnError:(NSError **)errPtr
{
BOOL pass = YES;
if(pass && !CFReadStreamOpen (theReadStream))
{
NSLog (@"AsyncSocket %p couldn't open read stream,", self);
pass = NO;
}
if(pass && !CFWriteStreamOpen (theWriteStream))
{
NSLog (@"AsyncSocket %p couldn't open write stream,", self);
pass = NO;
}
if(!pass)
{
if (errPtr) *errPtr = [self getStreamError];
}
return pass;
}
 
/**
* Called when read or write streams open.
* When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready.
**/
- (void)doStreamOpen
{
NSError *err = nil;
if ((theFlags & kDidCompleteOpenForRead) && (theFlags & kDidCompleteOpenForWrite))
{
// Get the socket.
if (![self setSocketFromStreamsAndReturnError: &err])
{
NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err);
[self closeWithError:err];
return;
}
// Stop the connection attempt timeout timer
[self endConnectTimeout];
if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)])
{
[theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]];
}
// Immediately deal with any already-queued requests.
[self maybeDequeueRead];
[self maybeDequeueWrite];
}
}
 
- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr
{
// Get the CFSocketNativeHandle from theReadStream
CFSocketNativeHandle native;
CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle);
if(nativeProp == NULL)
{
if (errPtr) *errPtr = [self getStreamError];
return NO;
}
CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native);
CFRelease(nativeProp);
CFSocketRef theSocket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL);
if(theSocket == NULL)
{
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
// Determine whether the connection was IPv4 or IPv6
CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
if(peeraddr == NULL)
{
NSLog(@"AsyncSocket couldn't determine IP version of socket");
CFRelease(theSocket);
if (errPtr) *errPtr = [self getSocketError];
return NO;
}
struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr);
if(sa->sa_family == AF_INET)
{
theSocket4 = theSocket;
}
else
{
theSocket6 = theSocket;
}
CFRelease(peeraddr);
 
return YES;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Disconnect Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
// Sends error message and disconnects
- (void)closeWithError:(NSError *)err
{
theFlags |= kClosingWithError;
if (theFlags & kDidPassConnectMethod)
{
// Try to salvage what data we can.
[self recoverUnreadData];
// Let the delegate know, so it can try to recover if it likes.
if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)])
{
[theDelegate onSocket:self willDisconnectWithError:err];
}
}
[self close];
}
 
// Prepare partially read data for recovery.
- (void)recoverUnreadData
{
if(theCurrentRead != nil)
{
// We never finished the current read.
// Check to see if it's a normal read packet (not AsyncSpecialPacket) and if it had read anything yet.
if(([theCurrentRead isKindOfClass:[AsyncReadPacket class]]) && (theCurrentRead->bytesDone > 0))
{
// We need to move its data into the front of the partial read buffer.
[partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0)
withBytes:[theCurrentRead->buffer bytes]
length:theCurrentRead->bytesDone];
}
}
[self emptyQueues];
}
 
- (void)emptyQueues
{
if (theCurrentRead != nil) [self endCurrentRead];
if (theCurrentWrite != nil) [self endCurrentWrite];
[theReadQueue removeAllObjects];
[theWriteQueue removeAllObjects];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil];
theFlags &= ~kDequeueReadScheduled;
theFlags &= ~kDequeueWriteScheduled;
}
 
/**
* Disconnects. This is called for both error and clean disconnections.
**/
- (void)close
{
// Empty queues
[self emptyQueues];
// Clear partialReadBuffer (pre-buffer and also unreadData buffer in case of error)
[partialReadBuffer replaceBytesInRange:NSMakeRange(0, [partialReadBuffer length]) withBytes:NULL length:0];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil];
// Stop the connection attempt timeout timer
if (theConnectTimer != nil)
{
[self endConnectTimeout];
}
// Close streams.
if (theReadStream != NULL)
{
[self runLoopUnscheduleReadStream];
CFReadStreamClose(theReadStream);
CFRelease(theReadStream);
theReadStream = NULL;
}
if (theWriteStream != NULL)
{
[self runLoopUnscheduleWriteStream];
CFWriteStreamClose(theWriteStream);
CFRelease(theWriteStream);
theWriteStream = NULL;
}
// Close sockets.
if (theSocket4 != NULL)
{
CFSocketInvalidate (theSocket4);
CFRelease (theSocket4);
theSocket4 = NULL;
}
if (theSocket6 != NULL)
{
CFSocketInvalidate (theSocket6);
CFRelease (theSocket6);
theSocket6 = NULL;
}
if (theSource4 != NULL)
{
[self runLoopRemoveSource:theSource4];
CFRelease (theSource4);
theSource4 = NULL;
}
if (theSource6 != NULL)
{
[self runLoopRemoveSource:theSource6];
CFRelease (theSource6);
theSource6 = NULL;
}
theRunLoop = NULL;
// If the client has passed the connect/accept method, then the connection has at least begun.
// Notify delegate that it is now ending.
BOOL shouldCallDelegate = (theFlags & kDidPassConnectMethod);
// Clear all flags (except the pre-buffering flag, which should remain as is)
theFlags &= kEnablePreBuffering;
if (shouldCallDelegate)
{
if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)])
{
[theDelegate onSocketDidDisconnect:self];
}
}
// Do not access any instance variables after calling onSocketDidDisconnect.
// This gives the delegate freedom to release us without returning here and crashing.
}
 
/**
* Disconnects immediately. Any pending reads or writes are dropped.
**/
- (void)disconnect
{
[self close];
}
 
/**
* Diconnects after all pending reads have completed.
**/
- (void)disconnectAfterReading
{
theFlags |= (kForbidReadsWrites | kDisconnectAfterReads);
[self maybeScheduleDisconnect];
}
 
/**
* Disconnects after all pending writes have completed.
**/
- (void)disconnectAfterWriting
{
theFlags |= (kForbidReadsWrites | kDisconnectAfterWrites);
[self maybeScheduleDisconnect];
}
 
/**
* Disconnects after all pending reads and writes have completed.
**/
- (void)disconnectAfterReadingAndWriting
{
theFlags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites);
[self maybeScheduleDisconnect];
}
 
/**
* Schedules a call to disconnect if possible.
* That is, if all writes have completed, and we're set to disconnect after writing,
* or if all reads have completed, and we're set to disconnect after reading.
**/
- (void)maybeScheduleDisconnect
{
BOOL shouldDisconnect = NO;
if(theFlags & kDisconnectAfterReads)
{
if(([theReadQueue count] == 0) && (theCurrentRead == nil))
{
if(theFlags & kDisconnectAfterWrites)
{
if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
{
shouldDisconnect = YES;
}
}
else
{
shouldDisconnect = YES;
}
}
}
else if(theFlags & kDisconnectAfterWrites)
{
if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
{
shouldDisconnect = YES;
}
}
if(shouldDisconnect)
{
[self performSelector:@selector(disconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
}
}
 
/**
* In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
* any data that's left on the socket.
**/
- (NSData *)unreadData
{
// Ensure this method will only return data in the event of an error
if(!(theFlags & kClosingWithError)) return nil;
if(theReadStream == NULL) return nil;
CFIndex totalBytesRead = [partialReadBuffer length];
BOOL error = NO;
while(!error && CFReadStreamHasBytesAvailable(theReadStream))
{
[partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE];
// Number of bytes to read is space left in packet buffer.
CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead;
// Read data into packet buffer
UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead );
CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead);
// Check results
if(bytesRead < 0)
{
error = YES;
}
else
{
totalBytesRead += bytesRead;
}
}
[partialReadBuffer setLength:totalBytesRead];
return partialReadBuffer;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Errors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
/**
* Returns a standard error object for the current errno value.
* Errno is used for low-level BSD socket errors.
**/
- (NSError *)getErrnoError
{
NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
}
 
/**
* Returns a standard error message for a CFSocket error.
* Unfortunately, CFSocket offers no feedback on its errors.
**/
- (NSError *)getSocketError
{
NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError",
@"AsyncSocket", [NSBundle mainBundle],
@"General CFSocket error", nil);
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info];
}
 
- (NSError *)getStreamError
{
CFStreamError err;
if (theReadStream != NULL)
{
err = CFReadStreamGetError (theReadStream);
if (err.error != 0) return [self errorFromCFStreamError: err];
}
if (theWriteStream != NULL)
{
err = CFWriteStreamGetError (theWriteStream);
if (err.error != 0) return [self errorFromCFStreamError: err];
}
return nil;
}
 
/**
* Returns a standard AsyncSocket abort error.
**/
- (NSError *)getAbortError
{
NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError",
@"AsyncSocket", [NSBundle mainBundle],
@"Connection canceled", nil);
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info];
}
 
/**
* Returns a standard AsyncSocket connect timeout error.
**/
- (NSError *)getConnectTimeoutError
{
NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketConnectTimeoutError",
@"AsyncSocket", [NSBundle mainBundle],
@"Attempt to connect to host timed out", nil);
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketConnectTimeoutError userInfo:info];
}
 
/**
* Returns a standard AsyncSocket maxed out error.
**/
- (NSError *)getReadMaxedOutError
{
NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError",
@"AsyncSocket", [NSBundle mainBundle],
@"Read operation reached set maximum length", nil);
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info];
}
 
/**
* Returns a standard AsyncSocket read timeout error.
**/
- (NSError *)getReadTimeoutError
{
NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError",
@"AsyncSocket", [NSBundle mainBundle],
@"Read operation timed out", nil);
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info];
}
 
/**
* Returns a standard AsyncSocket write timeout error.
**/
- (NSError *)getWriteTimeoutError
{
NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError",
@"AsyncSocket", [NSBundle mainBundle],
@"Write operation timed out", nil);
NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info];
}
 
- (NSError *)errorFromCFStreamError:(CFStreamError)err
{
if (err.domain == 0 && err.error == 0) return nil;
// Can't use switch; these constants aren't int literals.
NSString *domain = @"CFStreamError (unlisted domain)";
NSString *message = nil;
if(err.domain == kCFStreamErrorDomainPOSIX) {
domain = NSPOSIXErrorDomain;
}
else if(err.domain == kCFStreamErrorDomainMacOSStatus) {
domain = NSOSStatusErrorDomain;
}
else if(err.domain == kCFStreamErrorDomainMach) {
domain = NSMachErrorDomain;
}
else if(err.domain == kCFStreamErrorDomainNetDB)
{
domain = @"kCFStreamErrorDomainNetDB";
message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding];
}
else if(err.domain == kCFStreamErrorDomainNetServices) {
domain = @"kCFStreamErrorDomainNetServices";
}
else if(err.domain == kCFStreamErrorDomainSOCKS) {
domain = @"kCFStreamErrorDomainSOCKS";
}
else if(err.domain == kCFStreamErrorDomainSystemConfiguration) {
domain = @"kCFStreamErrorDomainSystemConfiguration";
}
else if(err.domain == kCFStreamErrorDomainSSL) {
domain = @"kCFStreamErrorDomainSSL";
}
NSDictionary *info = nil;
if(message != nil)
{
info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
}
return [NSError errorWithDomain:domain code:err.error userInfo:info];
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Diagnostics
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (BOOL)isConnected
{
return [self isSocketConnected] && [self areStreamsConnected];
}
 
- (NSString *)connectedHost
{
if(theSocket4)
return [self connectedHost:theSocket4];
else
return [self connectedHost:theSocket6];
}
 
- (UInt16)connectedPort
{
if(theSocket4)
return [self connectedPort:theSocket4];
else
return [self connectedPort:theSocket6];
}
 
- (NSString *)localHost
{
if(theSocket4)
return [self localHost:theSocket4];
else
return [self localHost:theSocket6];
}
 
- (UInt16)localPort
{
if(theSocket4)
return [self localPort:theSocket4];
else
return [self localPort:theSocket6];
}
 
- (NSString *)connectedHost:(CFSocketRef)theSocket
{
if (theSocket == NULL) return nil;
CFDataRef peeraddr;
NSString *peerstr = nil;
 
if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
{
peerstr = [self addressHost:peeraddr];
CFRelease (peeraddr);
}
 
return peerstr;
}
 
- (UInt16)connectedPort:(CFSocketRef)theSocket
{
if (theSocket == NULL) return 0;
CFDataRef peeraddr;
UInt16 peerport = 0;
 
if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
{
peerport = [self addressPort:peeraddr];
CFRelease (peeraddr);
}
 
return peerport;
}
 
- (NSString *)localHost:(CFSocketRef)theSocket
{
if (theSocket == NULL) return nil;
CFDataRef selfaddr;
NSString *selfstr = nil;
 
if((selfaddr = CFSocketCopyAddress(theSocket)))
{
selfstr = [self addressHost:selfaddr];
CFRelease (selfaddr);
}
 
return selfstr;
}
 
- (UInt16)localPort:(CFSocketRef)theSocket
{
if (theSocket == NULL) return 0;
CFDataRef selfaddr;
UInt16 selfport = 0;
 
if ((selfaddr = CFSocketCopyAddress(theSocket)))
{
selfport = [self addressPort:selfaddr];
CFRelease (selfaddr);
}
 
return selfport;
}
 
- (NSString *)addressHost:(CFDataRef)cfaddr
{
if (cfaddr == NULL) return nil;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr);
struct sockaddr_in *pSockAddrV4 = (struct sockaddr_in *)pSockAddr;
struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr;
 
const void *pAddr = (pSockAddr->sa_family == AF_INET) ?
(void *)(&(pSockAddrV4->sin_addr)) :
(void *)(&(pSockAddrV6->sin6_addr));
 
const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf));
if (pStr == NULL) [NSException raise: NSInternalInconsistencyException
format: @"Cannot convert address to string."];
 
return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding];
}
 
- (UInt16)addressPort:(CFDataRef)cfaddr
{
if (cfaddr == NULL) return 0;
struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr);
return ntohs (pAddr->sin_port);
}
 
- (NSData *)connectedAddress
{
CFSocketRef theSocket;
if (theSocket4)
theSocket = theSocket4;
else
theSocket = theSocket6;
if (theSocket == NULL) return nil;
CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
if (peeraddr == NULL) return nil;
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(peeraddr) length:CFDataGetLength(peeraddr)];
CFRelease(peeraddr);
return result;
#else
return [(NSData *)NSMakeCollectable(peeraddr) autorelease];
#endif
}
 
- (NSData *)localAddress
{
CFSocketRef theSocket;
if (theSocket4)
theSocket = theSocket4;
else
theSocket = theSocket6;
if (theSocket == NULL) return nil;
CFDataRef selfaddr = CFSocketCopyAddress(theSocket);
if (selfaddr == NULL) return nil;
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(selfaddr) length:CFDataGetLength(selfaddr)];
CFRelease(selfaddr);
return result;
#else
return [(NSData *)NSMakeCollectable(selfaddr) autorelease];
#endif
}
 
- (BOOL)isIPv4
{
return (theSocket4 != NULL);
}
 
- (BOOL)isIPv6
{
return (theSocket6 != NULL);
}
 
- (BOOL)isSocketConnected
{
if(theSocket4 != NULL)
return CFSocketIsValid(theSocket4);
else if(theSocket6 != NULL)
return CFSocketIsValid(theSocket6);
else
return NO;
}
 
- (BOOL)areStreamsConnected
{
CFStreamStatus s;
if (theReadStream != NULL)
{
s = CFReadStreamGetStatus (theReadStream);
if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) )
return NO;
}
else return NO;
if (theWriteStream != NULL)
{
s = CFWriteStreamGetStatus (theWriteStream);
if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) )
return NO;
}
else return NO;
return YES;
}
 
- (NSString *)description
{
static const char *statstr[] = {"not open","opening","open","reading","writing","at end","closed","has error"};
CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus(theReadStream) : 0;
CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus(theWriteStream) : 0;
NSString *peerstr, *selfstr;
CFDataRef peeraddr4 = NULL, peeraddr6 = NULL, selfaddr4 = NULL, selfaddr6 = NULL;
 
if (theSocket4 || theSocket6)
{
if (theSocket4) peeraddr4 = CFSocketCopyPeerAddress(theSocket4);
if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6);
if(theSocket4 && theSocket6)
{
peerstr = [NSString stringWithFormat: @"%@/%@ %u",
[self addressHost:peeraddr4], [self addressHost:peeraddr6], [self addressPort:peeraddr4]];
}
else if(theSocket4)
{
peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr4], [self addressPort:peeraddr4]];
}
else
{
peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]];
}
if(peeraddr4) CFRelease(peeraddr4);
if(peeraddr6) CFRelease(peeraddr6);
peeraddr4 = NULL;
peeraddr6 = NULL;
}
else peerstr = @"nowhere";
 
if (theSocket4 || theSocket6)
{
if (theSocket4) selfaddr4 = CFSocketCopyAddress (theSocket4);
if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6);
if (theSocket4 && theSocket6)
{
selfstr = [NSString stringWithFormat: @"%@/%@ %u",
[self addressHost:selfaddr4], [self addressHost:selfaddr6], [self addressPort:selfaddr4]];
}
else if (theSocket4)
{
selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr4], [self addressPort:selfaddr4]];
}
else
{
selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]];
}
 
if(selfaddr4) CFRelease(selfaddr4);
if(selfaddr6) CFRelease(selfaddr6);
selfaddr4 = NULL;
selfaddr6 = NULL;
}
else selfstr = @"nowhere";
NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:150];
[ms appendString:[NSString stringWithFormat:@"<AsyncSocket %p", self]];
[ms appendString:[NSString stringWithFormat:@" local %@ remote %@ ", selfstr, peerstr]];
unsigned readQueueCount = (unsigned)[theReadQueue count];
unsigned writeQueueCount = (unsigned)[theWriteQueue count];
[ms appendString:[NSString stringWithFormat:@"has queued %u reads %u writes, ", readQueueCount, writeQueueCount]];
 
if (theCurrentRead == nil)
[ms appendString: @"no current read, "];
else
{
int percentDone;
if ([theCurrentRead->buffer length] != 0)
percentDone = (float)theCurrentRead->bytesDone /
(float)[theCurrentRead->buffer length] * 100.0F;
else
percentDone = 100.0F;
 
[ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ",
(unsigned int)[theCurrentRead->buffer length],
theCurrentRead->bytesDone ? percentDone : 0]];
}
 
if (theCurrentWrite == nil)
[ms appendString: @"no current write, "];
else
{
int percentDone;
if ([theCurrentWrite->buffer length] != 0)
percentDone = (float)theCurrentWrite->bytesDone /
(float)[theCurrentWrite->buffer length] * 100.0F;
else
percentDone = 100.0F;
 
[ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ",
(unsigned int)[theCurrentWrite->buffer length],
theCurrentWrite->bytesDone ? percentDone : 0]];
}
[ms appendString:[NSString stringWithFormat:@"read stream %p %s, ", theReadStream, statstr[rs]]];
[ms appendString:[NSString stringWithFormat:@"write stream %p %s", theWriteStream, statstr[ws]]];
if(theFlags & kDisconnectAfterReads)
{
if(theFlags & kDisconnectAfterWrites)
[ms appendString: @", will disconnect after reads & writes"];
else
[ms appendString: @", will disconnect after reads"];
}
else if(theFlags & kDisconnectAfterWrites)
{
[ms appendString: @", will disconnect after writes"];
}
if (![self isConnected]) [ms appendString: @", not connected"];
 
[ms appendString:@">"];
 
return [ms autorelease];
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Reading
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
if(length == 0) return;
if(theFlags & kForbidReadsWrites) return;
NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length];
AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
timeout:timeout
tag:tag
readAllAvailable:NO
terminator:nil
maxLength:length];
 
[theReadQueue addObject:packet];
[self scheduleDequeueRead];
 
[packet release];
[buffer release];
}
 
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
[self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag];
}
 
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag
{
if(data == nil || [data length] == 0) return;
if(length >= 0 && length < [data length]) return;
if(theFlags & kForbidReadsWrites) return;
NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
timeout:timeout
tag:tag
readAllAvailable:NO
terminator:data
maxLength:length];
[theReadQueue addObject:packet];
[self scheduleDequeueRead];
[packet release];
[buffer release];
}
 
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
{
if (theFlags & kForbidReadsWrites) return;
NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
timeout:timeout
tag:tag
readAllAvailable:YES
terminator:nil
maxLength:-1];
[theReadQueue addObject:packet];
[self scheduleDequeueRead];
[packet release];
[buffer release];
}
 
/**
* Puts a maybeDequeueRead on the run loop.
* An assumption here is that selectors will be performed consecutively within their priority.
**/
- (void)scheduleDequeueRead
{
if((theFlags & kDequeueReadScheduled) == 0)
{
theFlags |= kDequeueReadScheduled;
[self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
}
}
 
/**
* This method starts a new read, if needed.
* It is called when a user requests a read,
* or when a stream opens that may have requested reads sitting in the queue, etc.
**/
- (void)maybeDequeueRead
{
// Unset the flag indicating a call to this method is scheduled
theFlags &= ~kDequeueReadScheduled;
// If we're not currently processing a read AND we have an available read stream
if((theCurrentRead == nil) && (theReadStream != NULL))
{
if([theReadQueue count] > 0)
{
// Dequeue the next object in the write queue
theCurrentRead = [[theReadQueue objectAtIndex:0] retain];
[theReadQueue removeObjectAtIndex:0];
if([theCurrentRead isKindOfClass:[AsyncSpecialPacket class]])
{
// Attempt to start TLS
theFlags |= kStartingReadTLS;
// This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set
[self maybeStartTLS];
}
else
{
// Start time-out timer
if(theCurrentRead->timeout >= 0.0)
{
theReadTimer = [NSTimer timerWithTimeInterval:theCurrentRead->timeout
target:self
selector:@selector(doReadTimeout:)
userInfo:nil
repeats:NO];
[self runLoopAddTimer:theReadTimer];
}
// Immediately read, if possible
[self doBytesAvailable];
}
}
else if(theFlags & kDisconnectAfterReads)
{
if(theFlags & kDisconnectAfterWrites)
{
if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
{
[self disconnect];
}
}
else
{
[self disconnect];
}
}
}
}
 
/**
* Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable().
* This method supports pre-buffering properly as well as the kSocketHasBytesAvailable flag.
**/
- (BOOL)hasBytesAvailable
{
if ((theFlags & kSocketHasBytesAvailable) || ([partialReadBuffer length] > 0))
{
return YES;
}
else
{
return CFReadStreamHasBytesAvailable(theReadStream);
}
}
 
/**
* Call this method in doBytesAvailable instead of CFReadStreamRead().
* This method support pre-buffering properly.
**/
- (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length
{
if([partialReadBuffer length] > 0)
{
// Determine the maximum amount of data to read
CFIndex bytesToRead = MIN(length, [partialReadBuffer length]);
// Copy the bytes from the buffer
memcpy(buffer, [partialReadBuffer bytes], bytesToRead);
// Remove the copied bytes from the buffer
[partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0];
return bytesToRead;
}
else
{
// Unset the "has-bytes-available" flag
theFlags &= ~kSocketHasBytesAvailable;
return CFReadStreamRead(theReadStream, buffer, length);
}
}
 
/**
* This method is called when a new read is taken from the read queue or when new data becomes available on the stream.
**/
- (void)doBytesAvailable
{
// If data is available on the stream, but there is no read request, then we don't need to process the data yet.
// Also, if there is a read request, but no read stream setup yet, we can't process any data yet.
if((theCurrentRead != nil) && (theReadStream != NULL))
{
// Note: This method is not called if theCurrentRead is an AsyncSpecialPacket (startTLS packet)
 
CFIndex totalBytesRead = 0;
BOOL done = NO;
BOOL socketError = NO;
BOOL maxoutError = NO;
while(!done && !socketError && !maxoutError && [self hasBytesAvailable])
{
BOOL didPreBuffer = NO;
// There are 3 types of read packets:
//
// 1) Read a specific length of data.
// 2) Read all available data.
// 3) Read up to a particular terminator.
if(theCurrentRead->readAllAvailableData == YES)
{
// We're reading all available data.
//
// Make sure there is at least READALL_CHUNKSIZE bytes available.
// We don't want to increase the buffer any more than this or we'll waste space.
// With prebuffering it's possible to read in a small chunk on the first read.
unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
[theCurrentRead->buffer increaseLengthBy:buffInc];
}
else if(theCurrentRead->term != nil)
{
// We're reading up to a terminator.
//
// We may only want to read a few bytes.
// Just enough to ensure we don't go past our term or over our max limit.
// Unless pre-buffering is enabled, in which case we may want to read in a larger chunk.
// If we already have data pre-buffered, we obviously don't want to pre-buffer it again.
// So in this case we'll just read as usual.
if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering))
{
unsigned maxToRead = [theCurrentRead readLengthForTerm];
unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
[theCurrentRead->buffer increaseLengthBy:bufInc];
}
else
{
didPreBuffer = YES;
unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm];
unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
[theCurrentRead->buffer increaseLengthBy:buffInc];
 
}
}
// Number of bytes to read is space left in packet buffer.
CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone;
// Read data into packet buffer
UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone);
CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead];
// Check results
if(bytesRead < 0)
{
socketError = YES;
}
else
{
// Update total amount read for the current read
theCurrentRead->bytesDone += bytesRead;
// Update total amount read in this method invocation
totalBytesRead += bytesRead;
}
 
// Is packet done?
if(theCurrentRead->readAllAvailableData != YES)
{
if(theCurrentRead->term != nil)
{
if(didPreBuffer)
{
// Search for the terminating sequence within the big chunk we just read.
CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead];
if(overflow > 0)
{
// Copy excess data into partialReadBuffer
NSMutableData *buffer = theCurrentRead->buffer;
const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow;
[partialReadBuffer appendBytes:overflowBuffer length:overflow];
// Update the bytesDone variable.
// Note: The completeCurrentRead method will trim the buffer for us.
theCurrentRead->bytesDone -= overflow;
}
done = (overflow >= 0);
}
else
{
// Search for the terminating sequence at the end of the buffer
int termlen = [theCurrentRead->term length];
if(theCurrentRead->bytesDone >= termlen)
{
const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen);
const void *seq = [theCurrentRead->term bytes];
done = (memcmp (buf, seq, termlen) == 0);
}
}
if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength)
{
// There's a set maxLength, and we've reached that maxLength without completing the read
maxoutError = YES;
}
}
else
{
// Done when (sized) buffer is full.
done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone);
}
}
// else readAllAvailable doesn't end until all readable is read.
}
if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0)
{
// Ran out of bytes, so the "read-all-available-data" type packet is done
done = YES;
}
 
if(done)
{
[self completeCurrentRead];
if (!socketError) [self scheduleDequeueRead];
}
else if(totalBytesRead > 0)
{
// We're not done with the readToLength or readToData yet, but we have read in some bytes
if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)])
{
[theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag];
}
}
 
if(socketError)
{
CFStreamError err = CFReadStreamGetError(theReadStream);
[self closeWithError:[self errorFromCFStreamError:err]];
return;
}
if(maxoutError)
{
[self closeWithError:[self getReadMaxedOutError]];
return;
}
}
}
 
// Ends current read and calls delegate.
- (void)completeCurrentRead
{
NSAssert(theCurrentRead, @"Trying to complete current read when there is no current read.");
[theCurrentRead->buffer setLength:theCurrentRead->bytesDone];
if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)])
{
[theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag];
}
if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected.
}
 
// Ends current read.
- (void)endCurrentRead
{
NSAssert(theCurrentRead, @"Trying to end current read when there is no current read.");
[theReadTimer invalidate];
theReadTimer = nil;
[theCurrentRead release];
theCurrentRead = nil;
}
 
- (void)doReadTimeout:(NSTimer *)timer
{
NSTimeInterval timeoutExtension = 0.0;
if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutReadWithTag:elapsed:bytesDone:)])
{
timeoutExtension = [theDelegate onSocket:self shouldTimeoutReadWithTag:theCurrentRead->tag
elapsed:theCurrentRead->timeout
bytesDone:theCurrentRead->bytesDone];
}
if(timeoutExtension > 0.0)
{
theCurrentRead->timeout += timeoutExtension;
theReadTimer = [NSTimer timerWithTimeInterval:timeoutExtension
target:self
selector:@selector(doReadTimeout:)
userInfo:nil
repeats:NO];
[self runLoopAddTimer:theReadTimer];
}
else
{
// Do not call endCurrentRead here.
// We must allow the delegate access to any partial read in the unreadData method.
[self closeWithError:[self getReadTimeoutError]];
}
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Writing
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
if (data == nil || [data length] == 0) return;
if (theFlags & kForbidReadsWrites) return;
AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
[theWriteQueue addObject:packet];
[self scheduleDequeueWrite];
[packet release];
}
 
- (void)scheduleDequeueWrite
{
if((theFlags & kDequeueWriteScheduled) == 0)
{
theFlags |= kDequeueWriteScheduled;
[self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
}
}
 
/**
* Conditionally starts a new write.
*
* IF there is not another write in process
* AND there is a write queued
* AND we have a write stream available
*
* This method also handles auto-disconnect post read/write completion.
**/
- (void)maybeDequeueWrite
{
// Unset the flag indicating a call to this method is scheduled
theFlags &= ~kDequeueWriteScheduled;
// If we're not currently processing a write AND we have an available write stream
if((theCurrentWrite == nil) && (theWriteStream != NULL))
{
if([theWriteQueue count] > 0)
{
// Dequeue the next object in the write queue
theCurrentWrite = [[theWriteQueue objectAtIndex:0] retain];
[theWriteQueue removeObjectAtIndex:0];
if([theCurrentWrite isKindOfClass:[AsyncSpecialPacket class]])
{
// Attempt to start TLS
theFlags |= kStartingWriteTLS;
// This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set
[self maybeStartTLS];
}
else
{
// Start time-out timer
if(theCurrentWrite->timeout >= 0.0)
{
theWriteTimer = [NSTimer timerWithTimeInterval:theCurrentWrite->timeout
target:self
selector:@selector(doWriteTimeout:)
userInfo:nil
repeats:NO];
[self runLoopAddTimer:theWriteTimer];
}
// Immediately write, if possible
[self doSendBytes];
}
}
else if(theFlags & kDisconnectAfterWrites)
{
if(theFlags & kDisconnectAfterReads)
{
if(([theReadQueue count] == 0) && (theCurrentRead == nil))
{
[self disconnect];
}
}
else
{
[self disconnect];
}
}
}
}
 
/**
* Call this method in doSendBytes instead of CFWriteStreamCanAcceptBytes().
* This method supports the kSocketCanAcceptBytes flag.
**/
- (BOOL)canAcceptBytes
{
if (theFlags & kSocketCanAcceptBytes)
{
return YES;
}
else
{
return CFWriteStreamCanAcceptBytes(theWriteStream);
}
}
 
- (void)doSendBytes
{
if((theCurrentWrite != nil) && (theWriteStream != NULL))
{
// Note: This method is not called if theCurrentWrite is an AsyncSpecialPacket (startTLS packet)
CFIndex totalBytesWritten = 0;
BOOL done = NO;
BOOL error = NO;
while (!done && !error && [self canAcceptBytes])
{
// Figure out what to write.
CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone;
CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE;
UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone);
 
// Write.
CFIndex bytesWritten = CFWriteStreamWrite(theWriteStream, writestart, bytesToWrite);
 
// Unset the "can accept bytes" flag
theFlags &= ~kSocketCanAcceptBytes;
// Check results
if (bytesWritten < 0)
{
error = YES;
}
else
{
// Update total amount read for the current write
theCurrentWrite->bytesDone += bytesWritten;
// Update total amount written in this method invocation
totalBytesWritten += bytesWritten;
// Is packet done?
done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone);
}
}
 
if(done)
{
[self completeCurrentWrite];
[self scheduleDequeueWrite];
}
else if(error)
{
CFStreamError err = CFWriteStreamGetError(theWriteStream);
[self closeWithError:[self errorFromCFStreamError:err]];
return;
}
else
{
// We're not done with the entire write, but we have written some bytes
if ([theDelegate respondsToSelector:@selector(onSocket:didWritePartialDataOfLength:tag:)])
{
[theDelegate onSocket:self didWritePartialDataOfLength:totalBytesWritten tag:theCurrentWrite->tag];
}
}
}
}
 
// Ends current write and calls delegate.
- (void)completeCurrentWrite
{
NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write.");
if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)])
{
[theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag];
}
if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected.
}
 
// Ends current write.
- (void)endCurrentWrite
{
NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write.");
[theWriteTimer invalidate];
theWriteTimer = nil;
[theCurrentWrite release];
theCurrentWrite = nil;
}
 
- (void)doWriteTimeout:(NSTimer *)timer
{
NSTimeInterval timeoutExtension = 0.0;
if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutWriteWithTag:elapsed:bytesDone:)])
{
timeoutExtension = [theDelegate onSocket:self shouldTimeoutWriteWithTag:theCurrentWrite->tag
elapsed:theCurrentWrite->timeout
bytesDone:theCurrentWrite->bytesDone];
}
if(timeoutExtension > 0.0)
{
theCurrentWrite->timeout += timeoutExtension;
theWriteTimer = [NSTimer timerWithTimeInterval:timeoutExtension
target:self
selector:@selector(doWriteTimeout:)
userInfo:nil
repeats:NO];
[self runLoopAddTimer:theWriteTimer];
}
else
{
[self closeWithError:[self getWriteTimeoutError]];
}
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Security
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (void)startTLS:(NSDictionary *)tlsSettings
{
if(tlsSettings == nil)
{
// Passing nil/NULL to CFReadStreamSetProperty will appear to work the same as passing an empty dictionary,
// but causes problems if we later try to fetch the remote host's certificate.
//
// To be exact, it causes the following to return NULL instead of the normal result:
// CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates)
//
// So we use an empty dictionary instead, which works perfectly.
tlsSettings = [NSDictionary dictionary];
}
AsyncSpecialPacket *packet = [[AsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings];
[theReadQueue addObject:packet];
[self scheduleDequeueRead];
[theWriteQueue addObject:packet];
[self scheduleDequeueWrite];
[packet release];
}
 
- (void)maybeStartTLS
{
// We can't start TLS until:
// - All queued reads prior to the user calling StartTLS are complete
// - All queued writes prior to the user calling StartTLS are complete
//
// We'll know these conditions are met when both kStartingReadTLS and kStartingWriteTLS are set
if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS))
{
AsyncSpecialPacket *tlsPacket = (AsyncSpecialPacket *)theCurrentRead;
BOOL didStartOnReadStream = CFReadStreamSetProperty(theReadStream, kCFStreamPropertySSLSettings,
(CFDictionaryRef)tlsPacket->tlsSettings);
BOOL didStartOnWriteStream = CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySSLSettings,
(CFDictionaryRef)tlsPacket->tlsSettings);
if(!didStartOnReadStream || !didStartOnWriteStream)
{
[self closeWithError:[self getSocketError]];
}
}
}
 
- (void)onTLSHandshakeSuccessful
{
if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS))
{
theFlags &= ~kStartingReadTLS;
theFlags &= ~kStartingWriteTLS;
if([theDelegate respondsToSelector:@selector(onSocketDidSecure:)])
{
[theDelegate onSocketDidSecure:self];
}
[self endCurrentRead];
[self endCurrentWrite];
[self scheduleDequeueRead];
[self scheduleDequeueWrite];
}
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark CF Callbacks
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
- (void)doCFSocketCallback:(CFSocketCallBackType)type
forSocket:(CFSocketRef)sock
withAddress:(NSData *)address
withData:(const void *)pData
{
NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
switch (type)
{
case kCFSocketConnectCallBack:
// The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed.
if(pData)
[self doSocketOpen:sock withCFSocketError:kCFSocketError];
else
[self doSocketOpen:sock withCFSocketError:kCFSocketSuccess];
break;
case kCFSocketAcceptCallBack:
[self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)];
break;
default:
NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %d.", self, type);
break;
}
}
 
- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream
{
NSParameterAssert(theReadStream != NULL);
CFStreamError err;
switch (type)
{
case kCFStreamEventOpenCompleted:
theFlags |= kDidCompleteOpenForRead;
[self doStreamOpen];
break;
case kCFStreamEventHasBytesAvailable:
if(theFlags & kStartingReadTLS) {
[self onTLSHandshakeSuccessful];
}
else {
theFlags |= kSocketHasBytesAvailable;
[self doBytesAvailable];
}
break;
case kCFStreamEventErrorOccurred:
case kCFStreamEventEndEncountered:
err = CFReadStreamGetError (theReadStream);
[self closeWithError: [self errorFromCFStreamError:err]];
break;
default:
NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %d.", self, type);
}
}
 
- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream
{
NSParameterAssert(theWriteStream != NULL);
CFStreamError err;
switch (type)
{
case kCFStreamEventOpenCompleted:
theFlags |= kDidCompleteOpenForWrite;
[self doStreamOpen];
break;
case kCFStreamEventCanAcceptBytes:
if(theFlags & kStartingWriteTLS) {
[self onTLSHandshakeSuccessful];
}
else {
theFlags |= kSocketCanAcceptBytes;
[self doSendBytes];
}
break;
case kCFStreamEventErrorOccurred:
case kCFStreamEventEndEncountered:
err = CFWriteStreamGetError (theWriteStream);
[self closeWithError: [self errorFromCFStreamError:err]];
break;
default:
NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %d.", self, type);
}
}
 
/**
* This is the callback we setup for CFSocket.
* This method does nothing but forward the call to it's Objective-C counterpart
**/
static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
[theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData];
[pool release];
}
 
/**
* This is the callback we setup for CFReadStream.
* This method does nothing but forward the call to it's Objective-C counterpart
**/
static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
[theSocket doCFReadStreamCallback:type forStream:stream];
[pool release];
}
 
/**
* This is the callback we setup for CFWriteStream.
* This method does nothing but forward the call to it's Objective-C counterpart
**/
static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
[theSocket doCFWriteStreamCallback:type forStream:stream];
[pool release];
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Class Methods
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
// Return line separators.
+ (NSData *)CRLFData
{
return [NSData dataWithBytes:"\x0D\x0A" length:2];
}
 
+ (NSData *)CRData
{
return [NSData dataWithBytes:"\x0D" length:1];
}
 
+ (NSData *)LFData
{
return [NSData dataWithBytes:"\x0A" length:1];
}
 
+ (NSData *)ZeroData
{
return [NSData dataWithBytes:"" length:1];
}
 
@end
/iKopter/trunk/Classes/Communication/MKConnection.h
0,0 → 1,48
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
 
@protocol MKConnectionDelegate < NSObject >
@optional
 
- (void) didConnectTo:(NSString *)hostOrDevice;
- (void) willDisconnectWithError:(NSError *)err;
- (void) didDisconnect;
- (void) didReadMkData:(NSData *)data;
 
@end
 
@protocol MKConnection
 
@property(assign) id<MKConnectionDelegate> delegate;
 
- (id) initWithDelegate:(id<MKConnectionDelegate>)delegate;
 
- (BOOL) connectTo:(NSString *)hostOrDevice error:(NSError **)err;
- (BOOL) isConnected;
- (void) disconnect;
 
- (void) writeMkData:(NSData *)data;
@end
/iKopter/trunk/Classes/Communication/MKConnectionController.h
0,0 → 1,89
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
#import "MKConnection.h"
#import "MKDatatypes.h"
 
extern NSString * const MKConnectedNotification;
extern NSString * const MKDisconnectedNotification;
extern NSString * const MKDisconnectErrorNotification;
 
extern NSString * const MKVersionNotification;
extern NSString * const MKDebugDataNotification;
extern NSString * const MKDebugLabelNotification;
extern NSString * const MKLcdMenuNotification;
extern NSString * const MKLcdNotification;
 
extern NSString * const MKReadSettingNotification;
extern NSString * const MKWriteSettingNotification;
extern NSString * const MKChangeSettingNotification;
 
extern NSString * const MKChannelValuesNotification;
 
extern NSString * const MKReadMixerNotification;
extern NSString * const MKWriteMixerNotification;
 
extern NSString * const MKOsdNotification;
 
 
@class MKHost;
 
@interface MKConnectionController : NSObject<MKConnectionDelegate> {
 
NSObject<MKConnection> * inputController;
NSString * hostOrDevice;
BOOL didPostConnectNotification;
 
MKAddress primaryDevice;
MKAddress currentDevice;
NSString * shortVersions[3];
NSString * longVersions[3];
}
 
@property(readonly) MKAddress primaryDevice;
@property(assign,readonly) MKAddress currentDevice;
 
+ (MKConnectionController *) sharedMKConnectionController;
 
- (void) start:(MKHost*)host;
- (void) stop;
 
- (BOOL) isRunning;
 
- (void) sendRequest:(NSData *)data;
 
- (BOOL) hasNaviCtrl;
- (void) activateNaviCtrl;
- (void) activateFlightCtrl;
- (void) activateMK3MAG;
- (void) activateMKGPS;
 
 
- (NSString *) shortVersionForAddress:(MKAddress)theAddress;
- (NSString *) longVersionForAddress:(MKAddress)theAddress;
 
 
@end
/iKopter/trunk/Classes/Communication/MKConnectionController.m
0,0 → 1,414
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "SynthesizeSingleton.h"
#import "MKConnectionController.h"
#import "NSData+MKCommandDecode.h"
#import "NSData+MKCommandEncode.h"
#import "NSData+MKPayloadDecode.h"
#import "MKDataConstants.h"
#import "MKHost.h"
#import "MKIpConnection.h"
 
// ///////////////////////////////////////////////////////////////////////////////
 
NSString * const MKConnectedNotification = @"MKConnectedNotification";
NSString * const MKDisconnectedNotification = @"MKDisconnectedNotification";
NSString * const MKDisconnectErrorNotification = @"MKDisconnectErrorNotification";
 
NSString * const MKVersionNotification = @"MKVersionNotification";
NSString * const MKDebugDataNotification = @"MKDebugDataNotification";
NSString * const MKDebugLabelNotification = @"MKDebugLabelNotification";
NSString * const MKLcdMenuNotification = @"MKLcdMenuNotification";
NSString * const MKLcdNotification = @"MKLcdNotification";
NSString * const MKReadSettingNotification = @"MKReadSettingNotification";
NSString * const MKWriteSettingNotification = @"MKWriteSettingNotification";
NSString * const MKChangeSettingNotification = @"MKChangeSettingNotification";
 
NSString * const MKChannelValuesNotification = @"MKChannelValuesNotification";
 
NSString * const MKReadMixerNotification = @"MKReadMixerNotification";
NSString * const MKWriteMixerNotification = @"MKWriteMixerNotification";
 
NSString * const MKOsdNotification = @"MKOsdNotification";
 
// ///////////////////////////////////////////////////////////////////////////////
 
@implementation MKConnectionController
 
// -------------------------------------------------------------------------------
 
//@synthesize hostOrDevice;
//@synthesize inputController;
 
@synthesize primaryDevice;
@synthesize currentDevice;
 
-(void) setCurrentDevice:(MKAddress)theAddress {
// if(primaryDevice==MKAddressNC) {
if(theAddress != currentDevice) {
currentDevice=theAddress;
if(currentDevice==MKAddressNC)
{
uint8_t bytes[5];
bytes[0] = 0x1B;
bytes[1] = 0x1B;
bytes[2] = 0x55;
bytes[3] = 0xAA;
bytes[4] = 0x00;
bytes[5] = '\r';
NSData * data = [NSData dataWithBytes:&bytes length:6];
[self sendRequest:data];
}
else {
uint8_t byte=0;
 
switch (currentDevice) {
case MKAddressFC:
byte=0;
break;
case MKAddressMK3MAg:
byte=1;
break;
}
 
NSData * data = [NSData dataWithCommand:MKCommandRedirectRequest
forAddress:MKAddressNC
payloadForByte:byte];
[self sendRequest:data];
}
}
// }
}
 
 
SYNTHESIZE_SINGLETON_FOR_CLASS(MKConnectionController);
 
// -------------------------------------------------------------------------------
 
- (void) dealloc {
[hostOrDevice release];
[inputController release];
[shortVersions[MKAddressFC] release];
[shortVersions[MKAddressNC] release];
[shortVersions[MKAddressMK3MAg] release];
[longVersions[MKAddressMK3MAg] release];
[longVersions[MKAddressNC] release];
[longVersions[MKAddressMK3MAg] release];
[super dealloc];
}
 
// -------------------------------------------------------------------------------
 
- (void) start:(MKHost*)host {
if (![inputController isConnected]) {
Class nsclass = NSClassFromString(host.connectionClass);
if (!nsclass) {
nsclass = [MKIpConnection class];
}
[inputController release];
inputController = [[nsclass alloc] initWithDelegate:self];
 
[hostOrDevice release];
hostOrDevice = [NSString stringWithFormat:@"%@:%d",host.address,host.port];
[hostOrDevice retain];
didPostConnectNotification = NO;
currentDevice=MKAddressAll;
primaryDevice=MKAddressAll;
NSError * err = nil;
if (![inputController connectTo:hostOrDevice error:&err]) {
NSLog(@"Error: %@", err);
}
}
}
 
- (void) stop {
if ([inputController isConnected]) {
DLog(@"disconnect");
[inputController disconnect];
}
// self.inputController=nil;
 
}
 
- (BOOL) isRunning;
{
return [inputController isConnected];
}
 
- (void) sendRequest:(NSData *)data;
{
DLog(@"%@",data);
NSString * msg = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
DLog(@"%@", msg);
 
[inputController writeMkData:data];
}
 
- (NSString *) shortVersionForAddress:(MKAddress)theAddress;
{
if (theAddress <= MKAddressAll || theAddress > MKAddressMK3MAg)
return nil;
return shortVersions[theAddress];
}
 
- (NSString *) longVersionForAddress:(MKAddress)theAddress;
{
if (theAddress <= MKAddressAll || theAddress > MKAddressMK3MAg)
return nil;
return longVersions[theAddress];
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
///////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL) hasNaviCtrl {
return primaryDevice==MKAddressNC;
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void) activateNaviCtrl {
if(primaryDevice!=MKAddressAll && ![self hasNaviCtrl])
return;
uint8_t bytes[5];
bytes[0] = 0x1B;
bytes[1] = 0x1B;
bytes[2] = 0x55;
bytes[3] = 0xAA;
bytes[4] = 0x00;
bytes[5] = '\r';
NSData * data = [NSData dataWithBytes:&bytes length:6];
[self sendRequest:data];
currentDevice=MKAddressNC;
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void) activateFlightCtrl {
 
if(![self hasNaviCtrl])
return;
 
uint8_t byte=0;
NSData * data = [NSData dataWithCommand:MKCommandRedirectRequest
forAddress:MKAddressNC
payloadForByte:byte];
[self sendRequest:data];
currentDevice=MKAddressFC;
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void) activateMK3MAG {
 
if(![self hasNaviCtrl])
return;
 
uint8_t byte=1;
NSData * data = [NSData dataWithCommand:MKCommandRedirectRequest
forAddress:MKAddressNC
payloadForByte:byte];
[self sendRequest:data];
currentDevice=MKAddressMK3MAg;
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void) activateMKGPS {
 
if(![self hasNaviCtrl])
return;
 
uint8_t byte=3;
NSData * data = [NSData dataWithCommand:MKCommandRedirectRequest
forAddress:MKAddressNC
payloadForByte:byte];
[self sendRequest:data];
currentDevice=MKAddressMKGPS;
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark MKInputDelegate
 
 
- (void) requestPrimaryDeviceVersion {
NSData * data = [NSData dataWithCommand:MKCommandVersionRequest
forAddress:MKAddressAll
payloadWithBytes:NULL
length:0];
[self sendRequest:data];
}
 
- (void) didConnectTo:(NSString *)hostOrDevice {
[self activateNaviCtrl];
[self performSelector:@selector(requestPrimaryDeviceVersion) withObject:self afterDelay:0.1];
}
 
- (void) willDisconnectWithError:(NSError *)err {
NSDictionary * d = [NSDictionary dictionaryWithObjectsAndKeys:err, @"error", nil];
 
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
 
[nc postNotificationName:MKDisconnectErrorNotification object:self userInfo:d];
}
 
- (void) didDisconnect {
NSDictionary * d = nil;
 
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
 
[nc postNotificationName:MKDisconnectedNotification object:self userInfo:d];
 
}
 
- (void) setVersionsFrom:(NSDictionary *)d forAddress:(MKAddress)address {
if (address != MKAddressAll) {
[shortVersions[address] release];
shortVersions[address] = [[d objectForKey:kMKDataKeyVersion] retain];
[longVersions[address] release];
longVersions[address] = [[d objectForKey:kMKDataKeyVersionShort] retain];
}
}
 
- (void) didReadMkData:(NSData *)data {
NSData * strData = [data subdataWithRange:NSMakeRange(1, [data length] - 1)];
 
NSString * msg = [[[NSString alloc] initWithData:strData encoding:NSASCIIStringEncoding] autorelease];
DLog(@">>%@<<", msg);
if ([strData isCrcOk]) {
// DLog(@"Data length %d",[strData length]);
 
NSData * payload = [strData payload];
MKAddress address = [strData address];
NSDictionary * d = nil;
NSString * n = nil;
 
switch ([strData command]) {
case MKCommandLcdMenuResponse:
n = MKLcdMenuNotification;
d = [payload decodeLcdMenuResponseForAddress:address];
break;
case MKCommandLcdResponse:
n = MKLcdNotification;
d = [payload decodeLcdResponseForAddress:address];
break;
case MKCommandDebugLabelResponse:
n = MKDebugLabelNotification;
d = [payload decodeAnalogLabelResponseForAddress:address];
break;
case MKCommandDebugValueResponse:
n = MKDebugDataNotification;
d = [payload decodeDebugDataResponseForAddress:address];
break;
case MKCommandChannelsValueResponse:
n = MKChannelValuesNotification;
d = [payload decodeChannelsDataResponse];
break;
case MKCommandReadSettingsResponse:
n = MKReadSettingNotification;
d = [payload decodeReadSettingResponse];
break;
case MKCommandWriteSettingsResponse:
n = MKWriteSettingNotification;
d = [payload decodeWriteSettingResponse];
break;
case MKCommandMixerReadResponse:
n = MKReadMixerNotification;
d = [payload decodeMixerReadResponse];
break;
case MKCommandMixerWriteResponse:
n = MKWriteMixerNotification;
d = [payload decodeMixerWriteResponse];
break;
 
case MKCommandChangeSettingsResponse:
n = MKChangeSettingNotification;
d = [payload decodeChangeSettingResponse];
break;
case MKCommandOsdResponse:
n = MKOsdNotification;
d = [payload decodeOsdResponse];
break;
case MKCommandVersionResponse:
n = MKVersionNotification;
d = [payload decodeVersionResponseForAddress:address];
[self setVersionsFrom:d forAddress:address];
if (!didPostConnectNotification) {
 
currentDevice=address;
primaryDevice=address;
DLog(@"Connected to primaryDevice %d, currentDevice %d",primaryDevice,currentDevice);
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:MKConnectedNotification object:self userInfo:nil];
}
break;
default:
break;
}
if (d)
DLog(@"(%d) %@", [d retainCount],d );
 
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:n object:self userInfo:d];
// if (d)
// DLog(@"(%d)", [d retainCount] );
 
} else {
NSString * msg = [[[NSString alloc] initWithData:strData encoding:NSASCIIStringEncoding] autorelease];
DLog(@"%@", msg);
}
}
 
@end
 
 
/iKopter/trunk/Classes/Communication/MKDataConstants.h
0,0 → 1,278
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#define kHostnameKey @"hostname"
#define kHostportKey @"hostport"
#define kUseDummyConnKey @"usedummy"
#define kUseConnClassKey @"useconnclass"
 
 
#define kMKDataKeyVersion @"version"
#define kMKDataKeyVersionShort @"versionShort"
#define kMKDataKeyMenuItem @"menuItem"
#define kMKDataKeyMaxMenuItem @"maxMenuItem"
#define kMKDataKeyMenuRows @"menuRows"
#define kMKDataKeyDebugData @"debugData"
#define kMKDataKeyLabel @"label"
#define kMKDataKeyIndex @"index"
#define kMKDataKeyChannels @"channels"
 
#define kMKDataKeyResult @"result"
#define kMKDataKeyRawValue @"rawvalue"
 
#define kMKDataKeyAddress @"address"
 
 
 
// unsigned char Kanalbelegung[12];
#define kKeyKanalbelegung00 @"Kanalbelegung00"
#define kKeyKanalbelegung01 @"Kanalbelegung01"
#define kKeyKanalbelegung02 @"Kanalbelegung02"
#define kKeyKanalbelegung03 @"Kanalbelegung03"
#define kKeyKanalbelegung04 @"Kanalbelegung04"
#define kKeyKanalbelegung05 @"Kanalbelegung05"
#define kKeyKanalbelegung06 @"Kanalbelegung06"
#define kKeyKanalbelegung07 @"Kanalbelegung07"
#define kKeyKanalbelegung08 @"Kanalbelegung08"
#define kKeyKanalbelegung09 @"Kanalbelegung09"
#define kKeyKanalbelegung10 @"Kanalbelegung10"
#define kKeyKanalbelegung11 @"Kanalbelegung11"
#define kKeyKanalbelegung12 @"Kanalbelegung12"
#define kKeyGlobalConfig @"GlobalConfig"
// unsigned char GlobalConfig;
// #define CFG_HOEHENREGELUNG 0x01
// #define CFG_HOEHEN_SCHALTER 0x02
// #define CFG_HEADING_HOLD 0x04
// #define CFG_KOMPASS_AKTIV 0x08
// #define CFG_KOMPASS_FIX 0x10
// #define CFG_GPS_AKTIV 0x20
// #define CFG_ACHSENKOPPLUNG_AKTIV 0x40
// #define CFG_DREHRATEN_BEGRENZER 0x80
#define kKeyGlobalConfig_HOEHENREGELUNG @"GlobalConfig_HOEHENREGELUNG"
#define kKeyGlobalConfig_HOEHEN_SCHALTER @"GlobalConfig_HOEHEN_SCHALTER"
#define kKeyGlobalConfig_HEADING_HOLD @"GlobalConfig_HEADING_HOLD"
#define kKeyGlobalConfig_KOMPASS_AKTIV @"GlobalConfig_KOMPASS_AKTIV"
#define kKeyGlobalConfig_KOMPASS_FIX @"GlobalConfig_KOMPASS_FIX"
#define kKeyGlobalConfig_GPS_AKTIV @"GlobalConfig_GPS_AKTIV"
#define kKeyGlobalConfig_ACHSENKOPPLUNG_AKTIV @"GlobalConfig_ACHSENKOPPLUNG_AKTIV"
#define kKeyGlobalConfig_DREHRATEN_BEGRENZER @"GlobalConfig_DREHRATEN_BEGRENZER"
// unsigned char Hoehe_MinGas; // Wert : 0-100
#define kKeyHoehe_MinGas @"Hoehe_MinGas"
// unsigned char Luftdruck_D; // Wert : 0-250
#define kKeyLuftdruck_D @"Luftdruck_D"
// unsigned char MaxHoehe; // Wert : 0-32
#define kKeyMaxHoehe @"MaxHoehe"
// unsigned char Hoehe_P; // Wert : 0-32
#define kKeyHoehe_P @"Hoehe_P"
// unsigned char Hoehe_Verstaerkung; // Wert : 0-50
#define kKeyHoehe_Verstaerkung @"Hoehe_Verstaerkung"
// unsigned char Hoehe_ACC_Wirkung; // Wert : 0-250
#define kKeyHoehe_ACC_Wirkung @"Hoehe_ACC_Wirkung"
// unsigned char Hoehe_HoverBand; // Wert : 0-250
#define kKeyHoehe_HoverBand @"Hoehe_HoverBand"
// unsigned char Hoehe_GPS_Z; // Wert : 0-250
#define kKeyHoehe_GPS_Z @"Hoehe_GPS_Z"
// unsigned char Hoehe_StickNeutralPoint;// Wert : 0-250
#define kKeyHoehe_StickNeutralPoint @"Hoehe_StickNeutralPoint"
// unsigned char Stick_P; // Wert : 1-6
#define kKeyStick_P @"Stick_P"
// unsigned char Stick_D; // Wert : 0-64
#define kKeyStick_D @"Stick_D"
// unsigned char Gier_P; // Wert : 1-20
#define kKeyGier_P @"Gier_P"
// unsigned char Gas_Min; // Wert : 0-32
#define kKeyGas_Min @"Gas_Min"
// unsigned char Gas_Max; // Wert : 33-250
#define kKeyGas_Max @"Gas_Max"
// unsigned char GyroAccFaktor; // Wert : 1-64
#define kKeyGyroAccFaktor @"GyroAccFaktor"
// unsigned char KompassWirkung; // Wert : 0-32
#define kKeyKompassWirkung @"KompassWirkung"
// unsigned char Gyro_P; // Wert : 10-250
#define kKeyGyro_P @"Gyro_P"
// unsigned char Gyro_I; // Wert : 0-250
#define kKeyGyro_I @"Gyro_I"
// unsigned char Gyro_D; // Wert : 0-250
#define kKeyGyro_D @"Gyro_D"
// unsigned char Gyro_Gier_P; // Wert : 10-250
#define kKeyGyro_Gier_P @"Gyro_Gier_P"
// unsigned char Gyro_Gier_I; // Wert : 0-250
#define kKeyGyro_Gier_I @"Gyro_Gier_I"
// unsigned char UnterspannungsWarnung; // Wert : 0-250
#define kKeyUnterspannungsWarnung @"UnterspannungsWarnung"
// unsigned char NotGas; // Wert : 0-250 //Gaswert bei Empfangsverlust
#define kKeyNotGas @"NotGas"
// unsigned char NotGasZeit; // Wert : 0-250 // Zeitbis auf NotGas geschaltet wird, wg. Rx-Problemen
#define kKeyNotGasZeit @"NotGasZeit"
// unsigned char Receiver; // 0= Summensignal, 1= Spektrum, 2 =Jeti, 3=ACT DSL, 4=ACT S3D
#define kKeyReceiver @"Receiver"
// unsigned char I_Faktor; // Wert : 0-250
#define kKeyI_Faktor @"I_Faktor"
// unsigned char UserParam1; // Wert : 0-250
#define kKeyUserParam1 @"UserParam1"
// unsigned char UserParam2; // Wert : 0-250
#define kKeyUserParam2 @"UserParam2"
// unsigned char UserParam3; // Wert : 0-250
#define kKeyUserParam3 @"UserParam3"
// unsigned char UserParam4; // Wert : 0-250
#define kKeyUserParam4 @"UserParam4"
// unsigned char ServoNickControl; // Wert : 0-250 // Stellung des Servos
#define kKeyServoNickControl @"ServoNickControl"
// unsigned char ServoNickComp; // Wert : 0-250 // Einfluss Gyro/Servo
#define kKeyServoNickComp @"ServoNickComp"
// unsigned char ServoNickMin; // Wert : 0-250 // Anschlag
#define kKeyServoNickMin @"ServoNickMin"
// unsigned char ServoNickMax; // Wert : 0-250 // Anschlag
#define kKeyServoNickMax @"ServoNickMax"
// //--- Seit V0.75
// unsigned char ServoRollControl; // Wert : 0-250 // Stellung des Servos
#define kKeyServoRollControl @"ServoRollControl"
// unsigned char ServoRollComp; // Wert : 0-250
#define kKeyServoRollComp @"ServoRollComp"
// unsigned char ServoRollMin; // Wert : 0-250
#define kKeyServoRollMin @"ServoRollMin"
// unsigned char ServoRollMax; // Wert : 0-250
#define kKeyServoRollMax @"ServoRollMax"
// //---
// unsigned char ServoNickRefresh; // Speed of the Servo
#define kKeyServoNickRefresh @"ServoNickRefresh"
// unsigned char Servo3; // Value or mapping of the Servo Output
#define kKeyServo3 @"Servo3"
// unsigned char Servo4; // Value or mapping of the Servo Output
#define kKeyServo4 @"Servo4"
// unsigned char Servo5; // Value or mapping of the Servo Output
#define kKeyServo5 @"Servo5"
// unsigned char LoopGasLimit; // Wert: 0-250 max. Gas während Looping
#define kKeyLoopGasLimit @"LoopGasLimit"
// unsigned char LoopThreshold; // Wert: 0-250 Schwelle für Stickausschlag
#define kKeyLoopThreshold @"LoopThreshold"
// unsigned char LoopHysterese; // Wert: 0-250 Hysterese für Stickausschlag
#define kKeyLoopHysterese @"LoopHysterese"
// unsigned char AchsKopplung1; // Wert: 0-250 Faktor, mit dem Gier die Achsen Roll und Nick koppelt (NickRollMitkopplung)
#define kKeyAchsKopplung1 @"AchsKopplung1"
// unsigned char AchsKopplung2; // Wert: 0-250 Faktor, mit dem Nick und Roll verkoppelt werden
#define kKeyAchsKopplung2 @"AchsKopplung2"
// unsigned char CouplingYawCorrection; // Wert: 0-250 Faktor, mit dem Nick und Roll verkoppelt werden
#define kKeyCouplingYawCorrection @"CouplingYawCorrection"
// unsigned char WinkelUmschlagNick; // Wert: 0-250 180?-Punkt
#define kKeyWinkelUmschlagNick @"WinkelUmschlagNick"
// unsigned char WinkelUmschlagRoll; // Wert: 0-250 180?-Punkt
#define kKeyWinkelUmschlagRoll @"WinkelUmschlagRoll"
// unsigned char GyroAccAbgleich; // 1/k (Koppel_ACC_Wirkung)
#define kKeyGyroAccAbgleich @"GyroAccAbgleich"
// unsigned char Driftkomp;
#define kKeyDriftkomp @"Driftkomp"
// unsigned char DynamicStability;
#define kKeyDynamicStability @"DynamicStability"
// unsigned char UserParam5; // Wert : 0-250
#define kKeyUserParam5 @"UserParam5"
// unsigned char UserParam6; // Wert : 0-250
#define kKeyUserParam6 @"UserParam6"
// unsigned char UserParam7; // Wert : 0-250
#define kKeyUserParam7 @"UserParam7"
// unsigned char UserParam8; // Wert : 0-250
#define kKeyUserParam8 @"UserParam8"
// //---Output ---------------------------------------------
// unsigned char J16Bitmask; // for the J16 Output
#define kKeyJ16Bitmask @"J16Bitmask"
// unsigned char J16Timing; // for the J16 Output
#define kKeyJ16Timing @"J16Timing"
// unsigned char J17Bitmask; // for the J17 Output
#define kKeyJ17Bitmask @"J17Bitmask"
// unsigned char J17Timing; // for the J17 Output
#define kKeyJ17Timing @"J17Timing"
// // seit version V0.75c
// unsigned char WARN_J16_Bitmask; // for the J16 Output
#define kKeyWARN_J16_Bitmask @"WARN_J16_Bitmask"
// unsigned char WARN_J17_Bitmask; // for the J17 Output
#define kKeyWARN_J17_Bitmask @"WARN_J17_Bitmask"
// //---NaviCtrl---------------------------------------------
// unsigned char NaviGpsModeControl; // Parameters for the Naviboard
#define kKeyNaviGpsModeControl @"NaviGpsModeControl"
// unsigned char NaviGpsGain;
#define kKeyNaviGpsGain @"NaviGpsGain"
// unsigned char NaviGpsP;
#define kKeyNaviGpsP @"NaviGpsP"
// unsigned char NaviGpsI;
#define kKeyNaviGpsI @"NaviGpsI"
// unsigned char NaviGpsD;
#define kKeyNaviGpsD @"NaviGpsD"
// unsigned char NaviGpsPLimit;
#define kKeyNaviGpsPLimit @"NaviGpsPLimit"
// unsigned char NaviGpsILimit;
#define kKeyNaviGpsILimit @"NaviGpsILimit"
// unsigned char NaviGpsDLimit;
#define kKeyNaviGpsDLimit @"NaviGpsDLimit"
// unsigned char NaviGpsACC;
#define kKeyNaviGpsACC @"NaviGpsACC"
// unsigned char NaviGpsMinSat;
#define kKeyNaviGpsMinSat @"NaviGpsMinSat"
// unsigned char NaviStickThreshold;
#define kKeyNaviStickThreshold @"NaviStickThreshold"
// unsigned char NaviWindCorrection;
#define kKeyNaviWindCorrection @"NaviWindCorrection"
// unsigned char NaviSpeedCompensation;
#define kKeyNaviSpeedCompensation @"NaviSpeedCompensation"
// unsigned char NaviOperatingRadius;
#define kKeyNaviOperatingRadius @"NaviOperatingRadius"
// unsigned char NaviAngleLimitation;
#define kKeyNaviAngleLimitation @"NaviAngleLimitation"
// unsigned char NaviPH_LoginTime;
#define kKeyNaviPH_LoginTime @"NaviPH_LoginTime"
// //---Ext.Ctrl---------------------------------------------
// unsigned char ExternalControl; // for serial Control
#define kKeyExternalControl @"ExternalControl"
// //------------------------------------------------
 
#define kKeyBitConfig @"BitConfig"
// unsigned char BitConfig; // (war Loop-Cfg) Bitcodiert: 0x01=oben, 0x02=unten, 0x04=links, 0x08=rechts / wird getrennt behandelt
// #define CFG1_LOOP_UP 0x01
// #define CFG1_LOOP_DOWN 0x02
// #define CFG1_LOOP_LEFT 0x04
// #define CFG1_LOOP_RIGHT 0x08
// #define CFG1_MOTOR_BLINK 0x10
// #define CFG1_MOTOR_OFF_LED1 0x20
// #define CFG1_MOTOR_OFF_LED2 0x40
#define kKeyBitConfig_LOOP_UP @"BitConfig_LOOP_UP"
#define kKeyBitConfig_LOOP_DOWN @"BitConfig_LOOP_DOWN"
#define kKeyBitConfig_LOOP_LEFT @"BitConfig_LOOP_LEFT"
#define kKeyBitConfig_LOOP_RIGHT @"BitConfig_LOOP_RIGHT"
#define kKeyBitConfig_MOTOR_BLINK @"BitConfig_MOTOR_BLINK"
#define kKeyBitConfig_MOTOR_OFF_LED1 @"BitConfig_MOTOR_OFF_LED1"
#define kKeyBitConfig_MOTOR_OFF_LED2 @"BitConfig_MOTOR_OFF_LED2"
// unsigned char ServoCompInvert; // // 0x01 = Nick, 0x02 = Roll 0 oder 1 // WICHTIG!!! am Ende lassen
#define kKeyServoCompInvert_Nick @"ServoCompInvert_Nick"
#define kKeyServoCompInvert_ROLL @"ServoCompInvert_ROLL"
// unsigned char ExtraConfig; // bitcodiert
// #define CFG2_HEIGHT_LIMIT 0x01
// #define CFG2_VARIO_BEEP 0x02
// #define CFG2_SENSITIVE_RC 0x04
#define kKeyExtraConfig @"ExtraConfig"
#define kKeyExtraConfig_HEIGHT_LIMIT @"ExtraConfig_HEIGHT_LIMIT"
#define kKeyExtraConfig_VARIO_BEEP @"ExtraConfig_VARIO_BEEP"
#define kKeyExtraConfig_SENSITIVE_RC @"ExtraConfig_SENSITIVE_RC"
// char Name[12];
#define kKeyName @"Name"
 
 
/iKopter/trunk/Classes/Communication/MKDatatypes.h
0,0 → 1,294
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
typedef enum {
MKAddressAll =0,
MKAddressFC =1,
MKAddressNC =2,
MKAddressMK3MAg=3,
MKAddressMKGPS =0XFE
} MKAddress;
 
static const int kMaxDebugData = 32;
 
typedef enum {
MKCommandDebugValueRequest='d',
MKCommandDebugValueResponse='D',
MKCommandDebugLabelRequest='a',
MKCommandDebugLabelResponse='A',
MKCommandVersionRequest='v',
MKCommandVersionResponse='V',
MKCommandLcdMenuRequest='l',
MKCommandLcdMenuResponse='L',
MKCommandLcdRequest='h',
MKCommandLcdResponse='H',
MKCommandReadSettingsRequest='q',
MKCommandReadSettingsResponse='Q',
MKCommandWriteSettingsRequest='s',
MKCommandWriteSettingsResponse='S',
MKCommandChangeSettingsRequest='f',
MKCommandChangeSettingsResponse='F',
MKCommandChannelsValueRequest='p',
MKCommandChannelsValueResponse='P',
MKCommandMixerReadRequest='n',
MKCommandMixerReadResponse='N',
MKCommandMixerWriteRequest='m',
MKCommandMixerWriteResponse='M',
MKCommandRedirectRequest='u',
MKCommandEngineTestRequest='t',
MKCommandOsdRequest='o',
MKCommandOsdResponse='O',
} MKCommandId;
 
 
typedef struct
{
uint8_t SWMajor;
uint8_t SWMinor;
uint8_t ProtoMajor;
uint8_t ProtoMinor;
uint8_t SWPatch;
uint8_t Reserved[5];
} VersionInfo;
 
typedef struct
{
uint8_t Digital[2];
uint16_t Analog[32]; // Debugvalues
} DebugOut;
 
typedef struct
{
char Revision;
char Name[12];
signed char Motor[16][4];
} Mixer;
 
typedef struct
{
unsigned char Kanalbelegung[12]; // GAS[0], GIER[1],NICK[2], ROLL[3], POTI1, POTI2, POTI3
unsigned char GlobalConfig; // 0x01=H?henregler aktiv,0x02=Kompass aktiv, 0x04=GPS aktiv, 0x08=Heading Hold aktiv
unsigned char Hoehe_MinGas; // Wert : 0-100
unsigned char Luftdruck_D; // Wert : 0-250
unsigned char MaxHoehe; // Wert : 0-32
unsigned char Hoehe_P; // Wert : 0-32
unsigned char Hoehe_Verstaerkung; // Wert : 0-50
unsigned char Hoehe_ACC_Wirkung; // Wert : 0-250
unsigned char Hoehe_HoverBand; // Wert : 0-250
unsigned char Hoehe_GPS_Z; // Wert : 0-250
unsigned char Hoehe_StickNeutralPoint;// Wert : 0-250
unsigned char Stick_P; // Wert : 1-6
unsigned char Stick_D; // Wert : 0-64
unsigned char Gier_P; // Wert : 1-20
unsigned char Gas_Min; // Wert : 0-32
unsigned char Gas_Max; // Wert : 33-250
unsigned char GyroAccFaktor; // Wert : 1-64
unsigned char KompassWirkung; // Wert : 0-32
unsigned char Gyro_P; // Wert : 10-250
unsigned char Gyro_I; // Wert : 0-250
unsigned char Gyro_D; // Wert : 0-250
unsigned char Gyro_Gier_P; // Wert : 10-250
unsigned char Gyro_Gier_I; // Wert : 0-250
unsigned char UnterspannungsWarnung; // Wert : 0-250
unsigned char NotGas; // Wert : 0-250 //Gaswert bei Emp?ngsverlust
unsigned char NotGasZeit; // Wert : 0-250 // Zeitbis auf NotGas geschaltet wird, wg. Rx-Problemen
unsigned char Receiver; // 0= Summensignal, 1= Spektrum, 2 =Jeti, 3=ACT DSL, 4=ACT S3D
unsigned char I_Faktor; // Wert : 0-250
unsigned char UserParam1; // Wert : 0-250
unsigned char UserParam2; // Wert : 0-250
unsigned char UserParam3; // Wert : 0-250
unsigned char UserParam4; // Wert : 0-250
unsigned char ServoNickControl; // Wert : 0-250 // Stellung des Servos
unsigned char ServoNickComp; // Wert : 0-250 // Einfluss Gyro/Servo
unsigned char ServoNickMin; // Wert : 0-250 // Anschlag
unsigned char ServoNickMax; // Wert : 0-250 // Anschlag
unsigned char ServoRollControl; // Wert : 0-250 // Stellung des Servos
unsigned char ServoRollComp; // Wert : 0-250
unsigned char ServoRollMin; // Wert : 0-250
unsigned char ServoRollMax; // Wert : 0-250
unsigned char ServoNickRefresh; // Speed of the Servo
unsigned char Servo3; // Value or mapping of the Servo Output
unsigned char Servo4; // Value or mapping of the Servo Output
unsigned char Servo5; // Value or mapping of the Servo Output
unsigned char LoopGasLimit; // Wert: 0-250 max. Gas w?hrend Looping
unsigned char LoopThreshold; // Wert: 0-250 Schwelle f?r Stickausschlag
unsigned char LoopHysterese; // Wert: 0-250 Hysterese f?r Stickausschlag
unsigned char AchsKopplung1; // Wert: 0-250 Faktor, mit dem Gier die Achsen Roll und Nick koppelt (NickRollMitkopplung)
unsigned char AchsKopplung2; // Wert: 0-250 Faktor, mit dem Nick und Roll verkoppelt werden
unsigned char CouplingYawCorrection; // Wert: 0-250 Faktor, mit dem Nick und Roll verkoppelt werden
unsigned char WinkelUmschlagNick; // Wert: 0-250 180?-Punkt
unsigned char WinkelUmschlagRoll; // Wert: 0-250 180?-Punkt
unsigned char GyroAccAbgleich; // 1/k (Koppel_ACC_Wirkung)
unsigned char Driftkomp;
unsigned char DynamicStability;
unsigned char UserParam5; // Wert : 0-250
unsigned char UserParam6; // Wert : 0-250
unsigned char UserParam7; // Wert : 0-250
unsigned char UserParam8; // Wert : 0-250
unsigned char J16Bitmask; // for the J16 Output
unsigned char J16Timing; // for the J16 Output
unsigned char J17Bitmask; // for the J17 Output
unsigned char J17Timing; // for the J17 Output
unsigned char WARN_J16_Bitmask; // for the J16 Output
unsigned char WARN_J17_Bitmask; // for the J17 Output
unsigned char NaviGpsModeControl; // Parameters for the Naviboard
unsigned char NaviGpsGain;
unsigned char NaviGpsP;
unsigned char NaviGpsI;
unsigned char NaviGpsD;
unsigned char NaviGpsPLimit;
unsigned char NaviGpsILimit;
unsigned char NaviGpsDLimit;
unsigned char NaviGpsACC;
unsigned char NaviGpsMinSat;
unsigned char NaviStickThreshold;
unsigned char NaviWindCorrection;
unsigned char NaviSpeedCompensation;
unsigned char NaviOperatingRadius;
unsigned char NaviAngleLimitation;
unsigned char NaviPH_LoginTime;
unsigned char ExternalControl; // for serial Control
unsigned char BitConfig; // (war Loop-Cfg) Bitcodiert: 0x01=oben, 0x02=unten, 0x04=links, 0x08=rechts / wird getrennt behandelt
unsigned char ServoCompInvert; // // 0x01 = Nick, 0x02 = Roll 0 oder 1 // WICHTIG!!! am Ende lassen
unsigned char ExtraConfig; // bitcodiert
char Name[12];
} MKSetting;
 
#define CFG_HOEHENREGELUNG 0x01
#define CFG_HOEHEN_SCHALTER 0x02
#define CFG_HEADING_HOLD 0x04
#define CFG_KOMPASS_AKTIV 0x08
#define CFG_KOMPASS_FIX 0x10
#define CFG_GPS_AKTIV 0x20
#define CFG_ACHSENKOPPLUNG_AKTIV 0x40
#define CFG_DREHRATEN_BEGRENZER 0x80
 
#define CFG_LOOP_OBEN 0x01
#define CFG_LOOP_UNTEN 0x02
#define CFG_LOOP_LINKS 0x04
#define CFG_LOOP_RECHTS 0x08
#define CFG_MOTOR_BLINK 0x10
#define CFG_MOTOR_OFF_LED1 0x20
#define CFG_MOTOR_OFF_LED2 0x40
#define CFG_RES4 0x80
 
#define CFG2_HEIGHT_LIMIT 0x01
#define CFG2_VARIO_BEEP 0x02
#define CFG_SENSITIVE_RC 0x04
 
#define CFG2_INVERT_NICK 0x01
#define CFG2_INVERT_ROLL 0x02
 
//////////////////////////////////////////////////////////////////////////////////////////
 
typedef struct
{
int16_t AngleNick; // in 0.1 deg
int16_t AngleRoll; // in 0.1 deg
int16_t Heading; // in 0.1 deg
uint8_t reserve[8];
} Data3D_t;
 
 
 
typedef struct
{
int32_t Longitude; // in 1E-7 deg
int32_t Latitude; // in 1E-7 deg
int32_t Altitude; // in mm
uint8_t Status; // validity of data
} __attribute__((packed)) GPS_Pos_t;
 
typedef struct
{
uint16_t Distance; // distance to target in dm
int16_t Bearing; // course to target in deg
} __attribute__((packed)) GPS_PosDev_t;
 
typedef struct
{
uint8_t Version; // version of the data structure
GPS_Pos_t CurrentPosition; // see ubx.h for details
GPS_Pos_t TargetPosition;
GPS_PosDev_t TargetPositionDeviation;
GPS_Pos_t HomePosition;
GPS_PosDev_t HomePositionDeviation;
uint8_t WaypointIndex; // index of current waypoints running from 0 to WaypointNumber-1
uint8_t WaypointNumber; // number of stored waypoints
uint8_t SatsInUse; // number of satellites used for position solution
int16_t Altimeter; // hight according to air pressure
int16_t Variometer; // climb(+) and sink(-) rate
uint16_t FlyingTime; // in seconds
uint8_t UBat; // Battery Voltage in 0.1 Volts
uint16_t GroundSpeed; // speed over ground in cm/s (2D)
int16_t Heading; // current flight direction in ° as angle to north
int16_t CompassHeading; // current compass value in °
int8_t AngleNick; // current Nick angle in 1°
int8_t AngleRoll; // current Rick angle in 1°
uint8_t RC_Quality; // RC_Quality
uint8_t FCFlags; // Flags from FC
uint8_t NCFlags; // Flags from NC
uint8_t Errorcode; // 0 --> okay
uint8_t OperatingRadius; // current operation radius around the Home Position in m
int16_t TopSpeed; // velocity in vertical direction in cm/s
uint8_t TargetHoldTime; // time in s to stay at the given target, counts down to 0 if target has been reached
uint8_t RC_RSSI; // Receiver signal strength (since version 2 added)
int16_t SetpointAltitude; // setpoint for altitude
uint8_t Gas; // for future use
uint16_t Current; // actual current in 0.1A steps
uint16_t UsedCapacity; // used capacity in mAh
} __attribute__((packed)) NaviData_t;
 
typedef struct
{
GPS_Pos_t Position; // the gps position of the waypoint, see ubx.h for details
int16_t Heading; // orientation, future implementation
uint8_t ToleranceRadius; // in meters, if the MK is within that range around the target, then the next target is triggered
uint8_t HoldTime; // in seconds, if the was once in the tolerance area around a WP, this time defies the delay before the next WP is triggered
uint8_t Event_Flag; // future emplementation
uint8_t reserve[12]; // reserve
} __attribute__((packed)) WayPoint_t;
 
 
#define NAVIDATA_VERSION 3
 
#define NC_FLAG_FREE 0x01
#define NC_FLAG_PH 0x02
#define NC_FLAG_CH 0x04
#define NC_FLAG_RANGE_LIMIT 0x08
#define NC_FLAG_NOSERIALLINK 0x10
#define NC_FLAG_TARGET_REACHED 0x20
#define NC_FLAG_MANUAL_CONTROL 0x40
#define NC_FLAG_8 0x80
 
#define FCFLAG_MOTOR_RUN 0x01
#define FCFLAG_FLY 0x02
#define FCFLAG_CALIBRATE 0x04
#define FCFLAG_START 0x08
#define FCFLAG_NOTLANDUNG 0x10
#define FCFLAG_LOWBAT 0x20
#define FCFLAG_SPI_RX_ERR 0x40
#define FCFLAG_I2CERR 0x80
 
/iKopter/trunk/Classes/Communication/MKFakeConnection.h
0,0 → 1,49
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
#import "MKConnection.h"
 
@class AsyncSocket;
 
@interface MKFakeConnection : NSObject<MKConnection> {
 
id<MKConnectionDelegate> delegate;
BOOL isConnected;
int lcdDataCounter;
int dbgDataCounter;
int menuCounter;
int menuPage;
int activeSetting;
NSMutableArray* settings;
}
 
@property (assign) id<MKConnectionDelegate> delegate;
 
- (id) init;
- (id) initWithDelegate:(id)delegate;
 
@end
/iKopter/trunk/Classes/Communication/MKFakeConnection.m
0,0 → 1,347
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
 
#import "MKFakeConnection.h"
#import "NSData+MKCommandDecode.h"
#import "NSData+MKCommandEncode.h"
#import "NSData+MKPayloadDecode.h"
#import "NSData+MKPayloadEncode.h"
 
#import "MKDataConstants.h"
 
static NSString * const MKDummyConnectionException = @"MKDummyConnectionException";
 
@interface MKFakeConnection (Private)
- (void) doConnect;
- (void) doDisconnect;
- (void) doResponseMkData:(NSData*)data;
 
@end
 
@implementation MKFakeConnection
 
#pragma mark Properties
 
@synthesize delegate;
 
#pragma mark Initialization
 
- (id) init {
return [self initWithDelegate:nil];
}
 
- (id) initWithDelegate:(id)theDelegate;
{
if (self = [super init]) {
self.delegate = theDelegate;
NSString *path = [[NSBundle mainBundle] pathForResource:@"AllSettings"
ofType:@"plist"];
settings = [[NSMutableArray arrayWithContentsOfFile:path] retain];
activeSetting = 3;
}
return self;
}
 
- (void) dealloc {
[settings release];
[super dealloc];
}
 
#pragma mark -
#pragma mark MKInput
 
- (BOOL) connectTo:(NSString *)hostOrDevice error:(NSError **)err;
{
if (delegate == nil) {
[NSException raise:MKDummyConnectionException
format:@"Attempting to connect without a delegate. Set a delegate first."];
}
 
NSArray * hostItems = [hostOrDevice componentsSeparatedByString:@":"];
if ( [hostItems count] != 2 ) {
[NSException raise:MKDummyConnectionException
format:@"Attempting to connect without a port. Set a port first."];
 
}
 
int port = [[hostItems objectAtIndex:1] intValue];
NSString * host = [hostItems objectAtIndex:0];
 
DLog(@"Try to connect to %@ on port %d", host, port);
 
[self performSelector:@selector(doConnect) withObject:nil afterDelay:0.5];
return YES;
}
 
- (BOOL) isConnected;
{
return isConnected;
}
 
- (void) disconnect;
{
[self performSelector:@selector(doDisconnect) withObject:nil afterDelay:0.1];
}
 
- (void) writeMkData:(NSData *)data;
{
[self performSelector:@selector(doResponseMkData:) withObject:[data retain] afterDelay:0.1];
}
 
#pragma mark -
 
 
- (NSData*) versionResponse;
{
VersionInfo v;
v.SWMajor = 0;
v.SWMinor = 78;
v.ProtoMajor = 3;
v.ProtoMinor = 1;
v.SWPatch = 3;
NSData * payload = [NSData dataWithBytes:(void*)&v length:sizeof(v)];
return payload;
}
 
- (NSData*) debugResponse;
{
DebugOut d;
NSData * payload = [NSData dataWithBytes:(void*)&d length:sizeof(d)];
return payload;
}
 
- (NSData*) channelResponse;
{
int16_t data[26];
for (int i=0; i<26; i++) {
data[i]=random()%250;
}
NSData * payload = [NSData dataWithBytes:(void*)&data length:sizeof(data)];
return payload;
}
 
 
 
- (NSData*) menuResponse:(NSData*)payload {
 
const char * bytes = [payload bytes];
uint8_t key=(uint8_t)bytes[0];
// uint8_t interval=(uint8_t)bytes[1];
if (key==0xFD) {
menuPage++;
} else if (key==0xFE) {
menuPage--;
}
menuPage %= 16;
menuCounter = 1;
NSString* screen=[NSString stringWithFormat:@"Page %02d (%d)---------12345678901234567890abcdefghijklmnopqrst++++++++++++++++++++",
menuPage,menuCounter];
 
NSData * newPayload = [screen dataUsingEncoding:NSASCIIStringEncoding];
 
[self performSelector:@selector(resendMenuResponse) withObject:nil afterDelay:0.5];
return newPayload;
}
 
- (void) resendMenuResponse {
 
NSString* screen=[NSString stringWithFormat:@"Page %02d (%d)---------12345678901234567890abcdefghijklmnopqrst++++++++++++++++++++",
menuPage,menuCounter];
NSData * newPayload = [screen dataUsingEncoding:NSASCIIStringEncoding];
NSData * rspData = [newPayload dataWithCommand:MKCommandLcdResponse forAddress:MKAddressFC];
if ( [delegate respondsToSelector:@selector(didReadMkData:)] ) {
[delegate didReadMkData:rspData];
}
if ( (++menuCounter)<2 ) {
[self performSelector:@selector(resendMenuResponse) withObject:nil afterDelay:0.5];
}
}
 
 
 
- (NSData*) changeSettingsResponse:(NSData*)payload {
const char * bytes = [payload bytes];
uint8_t index=(uint8_t)bytes[0];
activeSetting = index;
NSData * newPayload = [NSData dataWithBytes:(void*)&index length:sizeof(index)];
return newPayload;
}
 
- (NSData*) writeSettingResponse:(NSData*)payload {
 
NSDictionary* d = [payload decodeReadSettingResponse];
NSNumber* theIndex = [d objectForKey:kMKDataKeyIndex];
uint8_t index = [theIndex unsignedCharValue];
[settings replaceObjectAtIndex:index-1 withObject:d];
NSData * newPayload = [NSData dataWithBytes:(void*)&index length:sizeof(index)];
return newPayload;
}
 
- (NSData*) readSettingResponse:(NSData*)payload {
const char * bytes = [payload bytes];
uint8_t index=(uint8_t)bytes[0];
if (index==0xFF) {
index=activeSetting;
}
index--;
NSDictionary* d = [settings objectAtIndex:index];
DLog(@"%@",d);
NSData * newPayload = [NSData payloadForWriteSettingRequest:d];
return newPayload;
}
 
#pragma mark -
 
- (void) doConnect {
 
isConnected=YES;
if ( [delegate respondsToSelector:@selector(didConnectTo:)] ) {
[delegate didConnectTo:@"Dummy"];
}
 
NSData * data = [NSData dataWithCommand:MKCommandVersionRequest
forAddress:MKAddressAll
payloadWithBytes:NULL
length:0];
 
[self writeMkData:data];
}
 
 
- (void) doDisconnect {
isConnected=NO;
if ( [delegate respondsToSelector:@selector(didDisconnect)] ) {
[delegate didDisconnect];
}
}
 
- (void) doResponseMkData:(NSData*)data {
 
if ([data isCrcOk]) {
 
NSData * payload = [data payload];
// MKAddress address = [data address];
 
NSData * rspPayload;
MKCommandId rspCommand;
DLog(@"Need responde for command %c",[data command]);
 
switch ([data command]) {
/*
case MKCommandLcdMenuResponse:
n = MKLcdMenuNotification;
d = [payload decodeLcdMenuResponse];
break;
case MKCommandLcdResponse:
n = MKLcdNotification;
d = [payload decodeLcdResponse];
break;
case MKCommandDebugLabelResponse:
n = MKDebugLabelNotification;
d = [payload decodeAnalogLabelResponse];
break;
*/
case MKCommandLcdRequest:
rspPayload = [self menuResponse:payload];
rspCommand = MKCommandLcdResponse;
break;
case MKCommandDebugValueRequest:
rspPayload = [self debugResponse];
rspCommand = MKCommandDebugValueResponse;
break;
case MKCommandChannelsValueRequest:
rspPayload = [self channelResponse];
rspCommand = MKCommandChannelsValueResponse;
break;
case MKCommandVersionRequest:
rspPayload = [self versionResponse];
rspCommand = MKCommandVersionResponse;
break;
case MKCommandChangeSettingsRequest:
rspPayload = [self changeSettingsResponse:payload];
rspCommand = MKCommandChangeSettingsResponse;
break;
case MKCommandReadSettingsRequest:
rspPayload = [self readSettingResponse:payload];
rspCommand = MKCommandReadSettingsResponse;
break;
case MKCommandWriteSettingsRequest:
rspPayload = [self writeSettingResponse:payload];
rspCommand = MKCommandWriteSettingsResponse;
break;
case MKCommandEngineTestRequest:
DLog(@"Engine Test %@",payload);
[data release];
return;
default:
ALog(@"Unknown command %c",[data command]);
[data release];
return;
}
 
NSData * rspData = [rspPayload dataWithCommand:rspCommand forAddress:MKAddressFC];
if ( [delegate respondsToSelector:@selector(didReadMkData:)] ) {
[delegate didReadMkData:rspData];
}
}
 
[data release];
}
 
@end
/iKopter/trunk/Classes/Communication/MKIpConnection.h
0,0 → 1,37
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
#import "MKConnection.h"
 
@class AsyncSocket;
 
@interface MKIpConnection : NSObject<MKConnection> {
 
AsyncSocket * asyncSocket;
 
id<MKConnectionDelegate> delegate;
}
 
@end
/iKopter/trunk/Classes/Communication/MKIpConnection.m
0,0 → 1,160
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
 
#import "MKIpConnection.h"
#import "AsyncSocket.h"
 
static NSString * const MKIpConnectionException = @"MKIpConnectionException";
 
@implementation MKIpConnection
 
#pragma mark Properties
 
@synthesize delegate;
 
#pragma mark Initialization
 
- (id) init {
return [self initWithDelegate:nil];
}
 
- (id) initWithDelegate:(id<MKConnectionDelegate>)theDelegate;
{
if (self = [super init]) {
asyncSocket = [[AsyncSocket alloc] init];
[asyncSocket setDelegate:self];
self.delegate = theDelegate;
}
return self;
}
 
- (void) dealloc {
DLog("dealloc");
[asyncSocket release];
[super dealloc];
}
 
#pragma mark -
#pragma mark MKInput
 
- (BOOL) connectTo:(NSString *)hostOrDevice error:(NSError **)err;
{
if (delegate == nil) {
[NSException raise:MKIpConnectionException
format:@"Attempting to connect without a delegate. Set a delegate first."];
}
 
NSArray * hostItems = [hostOrDevice componentsSeparatedByString:@":"];
if ( [hostItems count] != 2 ) {
[NSException raise:MKIpConnectionException
format:@"Attempting to connect without a port. Set a port first."];
}
 
int port = [[hostItems objectAtIndex:1] intValue];
NSString * host = [hostItems objectAtIndex:0];
 
DLog(@"Try to connect to %@ on port %d", host, port);
return [asyncSocket connectToHost:host onPort:port withTimeout:30 error:err];
}
 
- (BOOL) isConnected;
{
return [asyncSocket isConnected];
}
 
- (void) disconnect;
{
DLog(@"Try to disconnect from %@ on port %d", [asyncSocket connectedHost], [asyncSocket connectedPort]);
[asyncSocket disconnect];
}
 
- (void) writeMkData:(NSData *)data;
{
// NSMutableData * newData = [data mutableCopy];
//
// [newData appendBytes:"\n" length:1];
 
[asyncSocket writeData:data withTimeout:-1 tag:0];
// [newData release];
}
 
#pragma mark -
#pragma mark AsyncSocketDelegate
 
- (BOOL) onSocketWillConnect:(AsyncSocket *)sock {
DLog(@"About to connect to %S", [sock connectedHost]);
return TRUE;
}
 
- (void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
{
DLog(@"Did connect to %@ on port %d", host, port);
 
if ( [delegate respondsToSelector:@selector(didConnectTo:)] ) {
[delegate didConnectTo:host];
}
 
 
DLog(@"Start reading the first data frame");
[sock readDataToData:[AsyncSocket CRData] withTimeout:-1 tag:0];
}
 
- (void) onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
{
if ( [delegate respondsToSelector:@selector(didReadMkData:)] ) {
[delegate didReadMkData:data];
}
// DLog(@"Did read data %@",data);
//
// DLog(@"Start reading the next data frame");
[sock readDataToData:[AsyncSocket CRData] withTimeout:-1 tag:0];
}
 
- (void) onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
{
DLog(@"Finished writing the next data frame");
}
 
- (void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
{
ALog(@"Disconnet with an error %@", err);
if ( [delegate respondsToSelector:@selector(willDisconnectWithError:)] ) {
[delegate willDisconnectWithError:err];
}
}
 
- (void) onSocketDidDisconnect:(AsyncSocket *)sock;
{
DLog(@"Disconnect from %@ on port %d", [asyncSocket connectedHost], [asyncSocket connectedPort]);
 
if ( [delegate respondsToSelector:@selector(didDisconnect)] ) {
[delegate didDisconnect];
}
}
 
@end
/iKopter/trunk/Classes/Communication/MKQmkIpConnection.h
0,0 → 1,32
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
#import "MKIpConnection.h"
 
@interface MKQmkIpConnection : MKIpConnection {
 
}
 
@end
/iKopter/trunk/Classes/Communication/MKQmkIpConnection.m
0,0 → 1,65
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "MKQmkIpConnection.h"
 
 
@implementation MKQmkIpConnection
 
- (void) writeMkData:(NSData *)data;
{
NSMutableData * newData = [data mutableCopy];
DLog(@"writeMkData, add \\n for QMK");
 
[newData appendBytes:"\n" length:1];
[super writeMkData:newData];
[newData release];
}
 
#pragma mark -
#pragma mark AsyncSocketDelegate
 
- (void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
{
[super onSocket:sock didConnectToHost:host port:port];
NSString* bundelName=[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
DLog(@"Sent QMK message");
NSString* welcomeMsg = [NSString stringWithFormat:@"$:99:101:%@:0\r",bundelName];
[self writeMkData:[welcomeMsg dataUsingEncoding:NSASCIIStringEncoding]];
NSString* dataMsg = @"$:99:106:VDALBH:0\r";
[self writeMkData:[dataMsg dataUsingEncoding:NSASCIIStringEncoding]];
}
 
- (void) onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
{
NSData * mkData = [data subdataWithRange:NSMakeRange(0, [data length] - 1)];
[super onSocket:sock didReadData:mkData withTag:tag];
}
 
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKCommandDecode.h
0,0 → 1,38
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
 
#import "MKDatatypes.h"
 
@interface NSData (MKCommandDecode)
 
- (BOOL) isMkData;
- (BOOL) isCrcOk;
- (MKAddress) address;
- (MKCommandId) command;
 
- (NSData *) payload;
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKCommandDecode.m
0,0 → 1,160
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "NSData+MKCommandDecode.h"
 
#define kInvalidMKCommand @"Invalid MK command"
 
// ///////////////////////////////////////////////////////////////////////////////
#pragma mark Helper funktions
 
static NSData * decode64(const char * inBuffer, int length)
{
unsigned char a, b, c, d;
unsigned char x, y, z;
int srcIdx = 0;
int dstIdx = 0;
NSMutableData * outData = [NSMutableData dataWithLength:length];
unsigned char * outBuffer = [outData mutableBytes];
if (inBuffer[srcIdx] != 0)
{
while (length != 0)
{
a = inBuffer[srcIdx++] - '=';
b = inBuffer[srcIdx++] - '=';
c = inBuffer[srcIdx++] - '=';
d = inBuffer[srcIdx++] - '=';
x = (a << 2) | (b >> 4);
y = ((b & 0x0f) << 4) | (c >> 2);
z = ((c & 0x03) << 6) | d;
if (length--) outBuffer[dstIdx++] = x; else break;
if (length--) outBuffer[dstIdx++] = y; else break;
if (length--) outBuffer[dstIdx++] = z; else break;
}
}
[outData setLength:dstIdx];
return outData;
}
 
// ///////////////////////////////////////////////////////////////////////////////
@implementation NSData (MKCommandDecode)
 
- (NSUInteger) frameLength;
{
NSUInteger frameLength = [self length];
const char * frameBytes = [self bytes];
if (frameLength > 0 && frameBytes[frameLength - 1] == '\r')
frameLength--;
return frameLength;
}
 
 
- (BOOL) isMkData
{
NSUInteger frameLength = [self frameLength];
const char * frameBytes = [self bytes];
if ( frameLength < 5)
{
NSLog(@"The frame length is to short %d. Frame is invalid", frameLength);
return NO;
}
if (frameBytes[0] != '#')
{
NSLog(@"The frame is no MK frame");
return NO;
}
return YES;
}
 
 
- (BOOL) isCrcOk
{
if (![self isMkData])
return NO;
NSUInteger frameLength = [self frameLength];
const uint8_t * frameBytes = [self bytes];
uint8_t crc2 = frameBytes[frameLength - 1];
uint8_t crc1 = frameBytes[frameLength - 2];
int crc = 0;
for (int i = 0; i < frameLength - 2; i++)
{
crc += frameBytes[i];
}
crc %= 4096;
if (crc1 == ('=' + (crc / 64)) && crc2 == ('=' + crc % 64))
return YES;
return NO;
}
 
- (MKAddress) address
{
if (![self isCrcOk])
[NSException raise:kInvalidMKCommand format:@"cannot get the address from a invalid MK frame"];
const char * frameBytes = [self bytes];
return (MKAddress)(frameBytes[1] - 'a');
}
 
- (MKCommandId) command
{
if (![self isCrcOk])
[NSException raise:kInvalidMKCommand format:@"cannot get the address from a invalid MK frame"];
const char * frameBytes = [self bytes];
return frameBytes[2];
}
 
 
- (NSData *) payload;
{
NSUInteger frameLength = [self frameLength];
const char * frameBytes = [self bytes];
int startIndex = 3;
int frameDataLength = frameLength - startIndex - 2;
return decode64(frameBytes + startIndex, frameDataLength);
}
 
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKCommandEncode.h
0,0 → 1,40
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
#import "MKDatatypes.h"
 
@interface NSData (MKCommandEncode)
 
- (NSData *) dataWithCommand:(MKCommandId)aCommand forAddress:(MKAddress)aAddress;
 
+ (NSData *) dataWithCommand:(MKCommandId)aCommand
forAddress:(MKAddress)aAddress
payloadForByte:(uint8_t)byte;
 
+ (NSData *) dataWithCommand:(MKCommandId)aCommand
forAddress:(MKAddress)aAddress
payloadWithBytes:(const void *)bytes
length:(NSUInteger)length;
@end
/iKopter/trunk/Classes/Communication/NSData+MKCommandEncode.m
0,0 → 1,119
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "NSData+MKCommandEncode.h"
 
// ///////////////////////////////////////////////////////////////////////////////
#pragma mark Helper funktions
 
static NSData * encode64(NSData * inData){
unsigned int dstIdx = 0;
unsigned int srcIdx = 0;
const unsigned char * inBuffer = [inData bytes];
unsigned int length = [inData length];
unsigned char a, b, c;
int outDataLength = (length * 4) / 3;
if (outDataLength % 4)
outDataLength += 4 - (outDataLength % 4);
NSMutableData * outData = [NSMutableData dataWithLength:outDataLength];
char * outBuffer = [outData mutableBytes];
while (length > 0) {
if (length) {
a = inBuffer[srcIdx++]; length--;
} else a = 0;
if (length) {
b = inBuffer[srcIdx++]; length--;
} else b = 0;
if (length) {
c = inBuffer[srcIdx++]; length--;
} else c = 0;
outBuffer[dstIdx++] = '=' + (a >> 2);
outBuffer[dstIdx++] = '=' + (((a & 0x03) << 4) | ((b & 0xf0) >> 4));
outBuffer[dstIdx++] = '=' + (((b & 0x0f) << 2) | ((c & 0xc0) >> 6));
outBuffer[dstIdx++] = '=' + ( c & 0x3f);
}
[outData setLength:dstIdx];
return outData;
}
 
// ///////////////////////////////////////////////////////////////////////////////
 
@implementation NSData (MKCommandEncode)
 
- (NSData *) dataWithCommand:(MKCommandId)aCommand forAddress:(MKAddress)aAddress;
{
NSMutableData * frameData = [NSMutableData dataWithLength:3];
char * frameBytes = [frameData mutableBytes];
frameBytes[0] = '#';
frameBytes[1] = 'a' + aAddress;
frameBytes[2] = aCommand;
[frameData appendData:encode64(self)];
NSUInteger frameLength = [frameData length];
frameBytes = [frameData mutableBytes];
int crc = 0;
for (int i = 0; i < frameLength; i++) {
crc += frameBytes[i];
}
crc %= 4096;
char tmpCrc1 = '=' + (crc / 64);
char tmpCrc2 = ('=' + crc % 64);
[frameData appendBytes:&tmpCrc1 length:1];
[frameData appendBytes:&tmpCrc2 length:1];
[frameData appendBytes:"\r" length:1];
return frameData;
}
 
+ (NSData *) dataWithCommand:(MKCommandId)aCommand forAddress:(MKAddress)aAddress payloadForByte:(uint8_t)byte {
NSData * payload = [NSData dataWithBytes:&byte length:1];
return [payload dataWithCommand:aCommand forAddress:aAddress];
}
 
+ (NSData *) dataWithCommand:(MKCommandId)aCommand
forAddress:(MKAddress)aAddress
payloadWithBytes:(const void *)bytes
length:(NSUInteger)length {
NSData * payload = [NSData dataWithBytes:bytes length:length];
return [payload dataWithCommand:aCommand forAddress:aAddress];
}
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKPayloadDecode.h
0,0 → 1,46
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
#import "MKDatatypes.h"
 
@interface NSData (MKPayloadDecode)
 
- (NSDictionary *) decodeLcdMenuResponseForAddress:(MKAddress)address;
- (NSDictionary *) decodeLcdResponseForAddress:(MKAddress)address;
- (NSDictionary *) decodeVersionResponseForAddress:(MKAddress)theAddress;
- (NSDictionary *) decodeAnalogLabelResponseForAddress:(MKAddress)address;
- (NSDictionary *) decodeDebugDataResponseForAddress:(MKAddress)address;
- (NSDictionary *) decodeChannelsDataResponse;
- (NSDictionary *) decodeOsdResponse;
 
 
 
@end
 
@interface NSData (MKPayloadDecodeSetting)
- (NSDictionary *) decodeReadSettingResponse;
- (NSDictionary *) decodeWriteSettingResponse;
- (NSDictionary *) decodeChangeSettingResponse;
@end
/iKopter/trunk/Classes/Communication/NSData+MKPayloadDecode.m
0,0 → 1,165
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "NSData+MKPayloadDecode.h"
#import "MKDataConstants.h"
 
static const NSString * HardwareType[] = { @"Default", @"FlightCtrl", @"NaviCtrl", @"MK3Mag" };
 
@implementation NSData (MKPayloadDecode)
 
//-------------------------------------------------------------------------------------------
- (NSString *) debugLabelWithIndex:(int *)theIndex {
const char * bytes = [self bytes];
*theIndex = (int)bytes[0];
int dataLength = [self length] < 16 ? [self length] : 16;
NSData * strData = [NSData dataWithBytesNoCopy:(void*)(++bytes) length:dataLength freeWhenDone:NO];
NSString * label = [[[NSString alloc] initWithData:strData encoding:NSASCIIStringEncoding] autorelease];
return [label stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
 
- (NSDictionary *) decodeAnalogLabelResponseForAddress:(MKAddress)address;
{
int index;
NSString * label = [self debugLabelWithIndex:&index];
return [NSDictionary dictionaryWithObjectsAndKeys:label, kMKDataKeyLabel, [NSNumber numberWithInt:index], kMKDataKeyIndex, nil];
}
 
//-------------------------------------------------------------------------------------------
- (NSDictionary *) decodeDebugDataResponseForAddress:(MKAddress)address;
{
DebugOut * debugData = (DebugOut *)[self bytes];
NSNumber* theAddress=[NSNumber numberWithInt:address];
NSMutableArray * targetArray = [[NSMutableArray alloc] initWithCapacity:32];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < 32; i++)
{
NSNumber *number = [NSNumber numberWithShort:debugData->Analog[i]];
[targetArray addObject:number];
}
[pool drain];
NSDictionary* responseDictionary=[NSDictionary dictionaryWithObjectsAndKeys:targetArray, kMKDataKeyDebugData,
theAddress, kMKDataKeyAddress, nil];
[targetArray release];
return responseDictionary;
}
 
//-------------------------------------------------------------------------------------------
- (NSDictionary *) decodeLcdMenuResponseForAddress:(MKAddress)address;
{
const char * bytes = [self bytes];
NSNumber* theAddress=[NSNumber numberWithInt:address];
NSNumber * menuItem = [NSNumber numberWithChar:bytes[0]];
NSNumber * maxMenuItem = [NSNumber numberWithChar:bytes[1]];
NSData * strData = [NSData dataWithBytesNoCopy:(char *)(bytes + 2) length:[self length] - 2 freeWhenDone:NO];
NSString * label = [[[NSString alloc] initWithData:strData encoding:NSASCIIStringEncoding] autorelease];
NSMutableArray * menuRows = [[NSMutableArray alloc] init];
[menuRows addObject:[label substringWithRange:NSMakeRange(0, 20)]];
[menuRows addObject:[label substringWithRange:NSMakeRange(20, 20)]];
[menuRows addObject:[label substringWithRange:NSMakeRange(40, 20)]];
[menuRows addObject:[label substringWithRange:NSMakeRange(60, 20)]];
NSDictionary* d =[NSDictionary dictionaryWithObjectsAndKeys:menuItem, kMKDataKeyMenuItem,
maxMenuItem, kMKDataKeyMaxMenuItem,
menuRows, kMKDataKeyMenuRows, theAddress, kMKDataKeyAddress, nil];
 
[menuRows autorelease];
return d;
}
 
//-------------------------------------------------------------------------------------------
- (NSDictionary *) decodeLcdResponseForAddress:(MKAddress)address;
{
NSString * label = [[NSString alloc] initWithData:self encoding:NSASCIIStringEncoding];
NSNumber* theAddress=[NSNumber numberWithInt:address];
NSMutableArray * menuRows = [[NSMutableArray alloc] init];
[menuRows addObject:[label substringWithRange:NSMakeRange(0, 20)]];
[menuRows addObject:[label substringWithRange:NSMakeRange(20, 20)]];
[menuRows addObject:[label substringWithRange:NSMakeRange(40, 20)]];
[menuRows addObject:[label substringWithRange:NSMakeRange(60, 20)]];
NSDictionary* d=[NSDictionary dictionaryWithObjectsAndKeys:menuRows, kMKDataKeyMenuRows,
theAddress, kMKDataKeyAddress, nil];
[label release];
[menuRows release];
return d;
}
 
//-------------------------------------------------------------------------------------------
- (NSDictionary *) decodeVersionResponseForAddress:(MKAddress)address;
{
const VersionInfo * version = [self bytes];
NSNumber* theAddress=[NSNumber numberWithInt:address];
NSString * versionStr = [NSString stringWithFormat:@"%@ %d.%d %c",
HardwareType[address],
version->SWMajor,
version->SWMinor,
(version->SWPatch + 'a')];
NSString * versionStrShort = [NSString stringWithFormat:@"%d.%d%c",
version->SWMajor,
version->SWMinor,
(version->SWPatch + 'a')];
return [NSDictionary dictionaryWithObjectsAndKeys:versionStr, kMKDataKeyVersion,
versionStrShort, kMKDataKeyVersionShort, theAddress, kMKDataKeyAddress, nil];
}
 
- (NSDictionary *) decodeChannelsDataResponse {
 
return [NSDictionary dictionaryWithObjectsAndKeys:self, kMKDataKeyChannels, nil];
 
}
 
 
- (NSDictionary *) decodeOsdResponse {
// NSValue* value = [NSValue valueWithBytes:[self bytes]
// objCType:@encode(NaviData_t)];
 
return [NSDictionary dictionaryWithObjectsAndKeys:self, kMKDataKeyRawValue, nil];
}
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKPayloadDecodeSetting.m
0,0 → 1,187
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "NSData+MKPayloadDecode.h"
#import "MKDataConstants.h"
 
@implementation NSData (MKPayloadDecodeSetting)
 
- (NSDictionary *) decodeReadSettingResponse {
const char * bytes = [self bytes];
NSNumber * index = [NSNumber numberWithChar:bytes[0]];
NSNumber * version = [NSNumber numberWithChar:bytes[1]];
MKSetting settings;
memcpy((unsigned char *)&settings, (unsigned char *)(bytes + 2), sizeof(settings));
NSMutableDictionary* d = [[[NSMutableDictionary alloc] initWithCapacity:112] autorelease];
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
[d setObject:index forKey:kMKDataKeyIndex];
[d setObject:version forKey:kMKDataKeyVersion];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[0]] forKey:kKeyKanalbelegung00];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[1]] forKey:kKeyKanalbelegung01];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[2]] forKey:kKeyKanalbelegung02];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[3]] forKey:kKeyKanalbelegung03];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[4]] forKey:kKeyKanalbelegung04];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[5]] forKey:kKeyKanalbelegung05];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[6]] forKey:kKeyKanalbelegung06];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[7]] forKey:kKeyKanalbelegung07];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[8]] forKey:kKeyKanalbelegung08];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[9]] forKey:kKeyKanalbelegung09];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[10]] forKey:kKeyKanalbelegung10];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Kanalbelegung[11]] forKey:kKeyKanalbelegung11];
[d setObject:[NSNumber numberWithUnsignedChar:settings.GlobalConfig] forKey:kKeyGlobalConfig];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_HOEHENREGELUNG ) != 0] forKey:kKeyGlobalConfig_HOEHENREGELUNG ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_HOEHEN_SCHALTER ) != 0] forKey:kKeyGlobalConfig_HOEHEN_SCHALTER ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_HEADING_HOLD ) != 0] forKey:kKeyGlobalConfig_HEADING_HOLD ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_KOMPASS_AKTIV ) != 0] forKey:kKeyGlobalConfig_KOMPASS_AKTIV ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_KOMPASS_FIX ) != 0] forKey:kKeyGlobalConfig_KOMPASS_FIX ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_GPS_AKTIV ) != 0] forKey:kKeyGlobalConfig_GPS_AKTIV ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_ACHSENKOPPLUNG_AKTIV ) != 0] forKey:kKeyGlobalConfig_ACHSENKOPPLUNG_AKTIV ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.GlobalConfig & CFG_DREHRATEN_BEGRENZER ) != 0] forKey:kKeyGlobalConfig_DREHRATEN_BEGRENZER ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_MinGas] forKey:kKeyHoehe_MinGas ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Luftdruck_D] forKey:kKeyLuftdruck_D ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.MaxHoehe] forKey:kKeyMaxHoehe ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_P] forKey:kKeyHoehe_P ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_Verstaerkung] forKey:kKeyHoehe_Verstaerkung ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_ACC_Wirkung] forKey:kKeyHoehe_ACC_Wirkung ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_HoverBand] forKey:kKeyHoehe_HoverBand ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_GPS_Z] forKey:kKeyHoehe_GPS_Z ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Hoehe_StickNeutralPoint] forKey:kKeyHoehe_StickNeutralPoint ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Stick_P] forKey:kKeyStick_P ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Stick_D] forKey:kKeyStick_D ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gier_P] forKey:kKeyGier_P ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gas_Min] forKey:kKeyGas_Min ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gas_Max] forKey:kKeyGas_Max ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.GyroAccFaktor] forKey:kKeyGyroAccFaktor ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.KompassWirkung] forKey:kKeyKompassWirkung ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gyro_P] forKey:kKeyGyro_P ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gyro_I] forKey:kKeyGyro_I ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gyro_D] forKey:kKeyGyro_D ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gyro_Gier_P] forKey:kKeyGyro_Gier_P ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Gyro_Gier_I] forKey:kKeyGyro_Gier_I ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UnterspannungsWarnung] forKey:kKeyUnterspannungsWarnung ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NotGas] forKey:kKeyNotGas ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NotGasZeit] forKey:kKeyNotGasZeit ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Receiver] forKey:kKeyReceiver ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.I_Faktor] forKey:kKeyI_Faktor ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam1] forKey:kKeyUserParam1 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam2] forKey:kKeyUserParam2 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam3] forKey:kKeyUserParam3 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam4] forKey:kKeyUserParam4 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoNickControl] forKey:kKeyServoNickControl ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoNickComp] forKey:kKeyServoNickComp ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoNickMin] forKey:kKeyServoNickMin ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoNickMax] forKey:kKeyServoNickMax ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoRollControl] forKey:kKeyServoRollControl ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoRollComp] forKey:kKeyServoRollComp ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoRollMin] forKey:kKeyServoRollMin ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoRollMax] forKey:kKeyServoRollMax ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ServoNickRefresh] forKey:kKeyServoNickRefresh ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Servo3] forKey:kKeyServo3 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Servo4] forKey:kKeyServo4 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Servo5] forKey:kKeyServo5 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.LoopGasLimit] forKey:kKeyLoopGasLimit ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.LoopThreshold] forKey:kKeyLoopThreshold ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.LoopHysterese] forKey:kKeyLoopHysterese ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.AchsKopplung1] forKey:kKeyAchsKopplung1 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.AchsKopplung2] forKey:kKeyAchsKopplung2 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.CouplingYawCorrection] forKey:kKeyCouplingYawCorrection ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.WinkelUmschlagNick] forKey:kKeyWinkelUmschlagNick ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.WinkelUmschlagRoll] forKey:kKeyWinkelUmschlagRoll ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.GyroAccAbgleich] forKey:kKeyGyroAccAbgleich ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.Driftkomp] forKey:kKeyDriftkomp ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.DynamicStability] forKey:kKeyDynamicStability ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam5] forKey:kKeyUserParam5 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam6] forKey:kKeyUserParam6 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam7] forKey:kKeyUserParam7 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.UserParam8] forKey:kKeyUserParam8 ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.J16Bitmask] forKey:kKeyJ16Bitmask ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.J16Timing] forKey:kKeyJ16Timing ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.J17Bitmask] forKey:kKeyJ17Bitmask ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.J17Timing] forKey:kKeyJ17Timing ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.WARN_J16_Bitmask] forKey:kKeyWARN_J16_Bitmask ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.WARN_J17_Bitmask] forKey:kKeyWARN_J17_Bitmask ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsModeControl] forKey:kKeyNaviGpsModeControl ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsGain] forKey:kKeyNaviGpsGain ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsP] forKey:kKeyNaviGpsP ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsI] forKey:kKeyNaviGpsI ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsD] forKey:kKeyNaviGpsD ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsPLimit] forKey:kKeyNaviGpsPLimit ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsILimit] forKey:kKeyNaviGpsILimit ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsDLimit] forKey:kKeyNaviGpsDLimit ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsACC] forKey:kKeyNaviGpsACC ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviGpsMinSat] forKey:kKeyNaviGpsMinSat ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviStickThreshold] forKey:kKeyNaviStickThreshold ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviWindCorrection] forKey:kKeyNaviWindCorrection ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviSpeedCompensation] forKey:kKeyNaviSpeedCompensation ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviOperatingRadius] forKey:kKeyNaviOperatingRadius ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviAngleLimitation] forKey:kKeyNaviAngleLimitation ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.NaviPH_LoginTime] forKey:kKeyNaviPH_LoginTime ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ExternalControl] forKey:kKeyExternalControl ];
[d setObject:[NSNumber numberWithUnsignedChar:settings.BitConfig] forKey:kKeyBitConfig];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_LOOP_OBEN ) != 0] forKey:kKeyBitConfig_LOOP_UP ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_LOOP_UNTEN ) != 0] forKey:kKeyBitConfig_LOOP_DOWN ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_LOOP_LINKS ) != 0] forKey:kKeyBitConfig_LOOP_LEFT ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_LOOP_RECHTS ) != 0] forKey:kKeyBitConfig_LOOP_RIGHT ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_MOTOR_BLINK ) != 0] forKey:kKeyBitConfig_MOTOR_BLINK ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_MOTOR_OFF_LED1) != 0] forKey:kKeyBitConfig_MOTOR_OFF_LED1];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG_MOTOR_OFF_LED2) != 0] forKey:kKeyBitConfig_MOTOR_OFF_LED2];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG2_INVERT_NICK) != 0] forKey:kKeyServoCompInvert_Nick];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.BitConfig & CFG2_INVERT_ROLL) != 0] forKey:kKeyServoCompInvert_ROLL];
[d setObject:[NSNumber numberWithUnsignedChar:settings.ExtraConfig & CFG2_HEIGHT_LIMIT] forKey:kKeyExtraConfig];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.ExtraConfig & CFG2_HEIGHT_LIMIT) != 0] forKey:kKeyExtraConfig_HEIGHT_LIMIT];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.ExtraConfig & CFG2_VARIO_BEEP ) != 0] forKey:kKeyExtraConfig_VARIO_BEEP ];
[d setObject:[NSNumber numberWithUnsignedChar:(settings.ExtraConfig & CFG_SENSITIVE_RC ) != 0] forKey:kKeyExtraConfig_SENSITIVE_RC];
NSString* name=[NSString stringWithCString:settings.Name encoding:NSASCIIStringEncoding];
[d setObject:name forKey:kKeyName];
[pool drain];
return d;
}
 
- (NSDictionary *) decodeWriteSettingResponse {
const char * bytes = [self bytes];
NSNumber * theIndex = [NSNumber numberWithChar:bytes[0]];
return [NSDictionary dictionaryWithObjectsAndKeys:theIndex, kMKDataKeyIndex, nil];
}
 
- (NSDictionary *) decodeChangeSettingResponse {
const char * bytes = [self bytes];
NSNumber * theIndex = [NSNumber numberWithChar:bytes[0]];
return [NSDictionary dictionaryWithObjectsAndKeys:theIndex, kMKDataKeyIndex, nil];
}
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKPayloadEncode.h
0,0 → 1,36
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
 
 
@interface NSData (MKPayloadEncode)
 
@end
 
@interface NSData (MKPayloadEncodeSettings)
 
+ (NSData *) payloadForWriteSettingRequest:(NSDictionary *)theDictionary;
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKPayloadEncode.m
0,0 → 1,29
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "NSData+MKPayloadEncode.h"
 
@implementation NSData (MKPayloadEncode)
 
@end
/iKopter/trunk/Classes/Communication/NSData+MKPayloadEncodeSetting.m
0,0 → 1,179
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "NSData+MKPayloadEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
@implementation NSData (MKPayloadEncodeSetting)
 
+ (NSData *) payloadForWriteSettingRequest:(NSDictionary *)theDictionary;
{
MKSetting setting;
setting.Kanalbelegung[ 0]=[[theDictionary objectForKey:kKeyKanalbelegung00] unsignedCharValue];
setting.Kanalbelegung[ 1]=[[theDictionary objectForKey:kKeyKanalbelegung01] unsignedCharValue];
setting.Kanalbelegung[ 2]=[[theDictionary objectForKey:kKeyKanalbelegung02] unsignedCharValue];
setting.Kanalbelegung[ 3]=[[theDictionary objectForKey:kKeyKanalbelegung03] unsignedCharValue];
setting.Kanalbelegung[ 4]=[[theDictionary objectForKey:kKeyKanalbelegung04] unsignedCharValue];
setting.Kanalbelegung[ 5]=[[theDictionary objectForKey:kKeyKanalbelegung05] unsignedCharValue];
setting.Kanalbelegung[ 6]=[[theDictionary objectForKey:kKeyKanalbelegung06] unsignedCharValue];
setting.Kanalbelegung[ 7]=[[theDictionary objectForKey:kKeyKanalbelegung07] unsignedCharValue];
setting.Kanalbelegung[ 8]=[[theDictionary objectForKey:kKeyKanalbelegung08] unsignedCharValue];
setting.Kanalbelegung[ 9]=[[theDictionary objectForKey:kKeyKanalbelegung09] unsignedCharValue];
setting.Kanalbelegung[10]=[[theDictionary objectForKey:kKeyKanalbelegung10] unsignedCharValue];
setting.Kanalbelegung[11]=[[theDictionary objectForKey:kKeyKanalbelegung11] unsignedCharValue];
setting.GlobalConfig=0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_HOEHENREGELUNG ] boolValue]?CFG_HOEHENREGELUNG :0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_HOEHEN_SCHALTER ] boolValue]?CFG_HOEHEN_SCHALTER :0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_HEADING_HOLD ] boolValue]?CFG_HEADING_HOLD :0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_KOMPASS_AKTIV ] boolValue]?CFG_KOMPASS_AKTIV :0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_KOMPASS_FIX ] boolValue]?CFG_KOMPASS_FIX :0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_GPS_AKTIV ] boolValue]?CFG_GPS_AKTIV :0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_ACHSENKOPPLUNG_AKTIV] boolValue]?CFG_ACHSENKOPPLUNG_AKTIV:0;
setting.GlobalConfig|= [[theDictionary objectForKey:kKeyGlobalConfig_DREHRATEN_BEGRENZER ] boolValue]?CFG_DREHRATEN_BEGRENZER :0;
setting.Hoehe_MinGas=[[theDictionary objectForKey:kKeyHoehe_MinGas ] unsignedCharValue];
setting.Luftdruck_D=[[theDictionary objectForKey:kKeyLuftdruck_D ] unsignedCharValue];
setting.MaxHoehe=[[theDictionary objectForKey:kKeyMaxHoehe ] unsignedCharValue];
setting.Hoehe_P=[[theDictionary objectForKey:kKeyHoehe_P ] unsignedCharValue];
setting.Hoehe_Verstaerkung=[[theDictionary objectForKey:kKeyHoehe_Verstaerkung ] unsignedCharValue];
setting.Hoehe_ACC_Wirkung=[[theDictionary objectForKey:kKeyHoehe_ACC_Wirkung ] unsignedCharValue];
setting.Hoehe_HoverBand=[[theDictionary objectForKey:kKeyHoehe_HoverBand ] unsignedCharValue];
setting.Hoehe_GPS_Z=[[theDictionary objectForKey:kKeyHoehe_GPS_Z ] unsignedCharValue];
setting.Hoehe_StickNeutralPoint=[[theDictionary objectForKey:kKeyHoehe_StickNeutralPoint ] unsignedCharValue];
setting.Stick_P=[[theDictionary objectForKey:kKeyStick_P ] unsignedCharValue];
setting.Stick_D=[[theDictionary objectForKey:kKeyStick_D ] unsignedCharValue];
setting.Gier_P=[[theDictionary objectForKey:kKeyGier_P ] unsignedCharValue];
setting.Gas_Min=[[theDictionary objectForKey:kKeyGas_Min ] unsignedCharValue];
setting.Gas_Max=[[theDictionary objectForKey:kKeyGas_Max ] unsignedCharValue];
setting.GyroAccFaktor=[[theDictionary objectForKey:kKeyGyroAccFaktor ] unsignedCharValue];
setting.KompassWirkung=[[theDictionary objectForKey:kKeyKompassWirkung ] unsignedCharValue];
setting.Gyro_P=[[theDictionary objectForKey:kKeyGyro_P ] unsignedCharValue];
setting.Gyro_I=[[theDictionary objectForKey:kKeyGyro_I ] unsignedCharValue];
setting.Gyro_D=[[theDictionary objectForKey:kKeyGyro_D ] unsignedCharValue];
setting.Gyro_Gier_P=[[theDictionary objectForKey:kKeyGyro_Gier_P ] unsignedCharValue];
setting.Gyro_Gier_I=[[theDictionary objectForKey:kKeyGyro_Gier_I ] unsignedCharValue];
setting.UnterspannungsWarnung=[[theDictionary objectForKey:kKeyUnterspannungsWarnung ] unsignedCharValue];
setting.NotGas=[[theDictionary objectForKey:kKeyNotGas ] unsignedCharValue];
setting.NotGasZeit=[[theDictionary objectForKey:kKeyNotGasZeit ] unsignedCharValue];
setting.Receiver=[[theDictionary objectForKey:kKeyReceiver ] unsignedCharValue];
setting.I_Faktor=[[theDictionary objectForKey:kKeyI_Faktor ] unsignedCharValue];
setting.UserParam1=[[theDictionary objectForKey:kKeyUserParam1 ] unsignedCharValue];
setting.UserParam2=[[theDictionary objectForKey:kKeyUserParam2 ] unsignedCharValue];
setting.UserParam3=[[theDictionary objectForKey:kKeyUserParam3 ] unsignedCharValue];
setting.UserParam4=[[theDictionary objectForKey:kKeyUserParam4 ] unsignedCharValue];
setting.ServoNickControl=[[theDictionary objectForKey:kKeyServoNickControl ] unsignedCharValue];
setting.ServoNickComp=[[theDictionary objectForKey:kKeyServoNickComp ] unsignedCharValue];
setting.ServoNickMin=[[theDictionary objectForKey:kKeyServoNickMin ] unsignedCharValue];
setting.ServoNickMax=[[theDictionary objectForKey:kKeyServoNickMax ] unsignedCharValue];
setting.ServoRollControl=[[theDictionary objectForKey:kKeyServoRollControl ] unsignedCharValue];
setting.ServoRollComp=[[theDictionary objectForKey:kKeyServoRollComp ] unsignedCharValue];
setting.ServoRollMin=[[theDictionary objectForKey:kKeyServoRollMin ] unsignedCharValue];
setting.ServoRollMax=[[theDictionary objectForKey:kKeyServoRollMax ] unsignedCharValue];
setting.ServoNickRefresh=[[theDictionary objectForKey:kKeyServoNickRefresh ] unsignedCharValue];
setting.Servo3=[[theDictionary objectForKey:kKeyServo3 ] unsignedCharValue];
setting.Servo4=[[theDictionary objectForKey:kKeyServo4 ] unsignedCharValue];
setting.Servo5=[[theDictionary objectForKey:kKeyServo5 ] unsignedCharValue];
setting.LoopGasLimit=[[theDictionary objectForKey:kKeyLoopGasLimit ] unsignedCharValue];
setting.LoopThreshold=[[theDictionary objectForKey:kKeyLoopThreshold ] unsignedCharValue];
setting.LoopHysterese=[[theDictionary objectForKey:kKeyLoopHysterese ] unsignedCharValue];
setting.AchsKopplung1=[[theDictionary objectForKey:kKeyAchsKopplung1 ] unsignedCharValue];
setting.AchsKopplung2=[[theDictionary objectForKey:kKeyAchsKopplung2 ] unsignedCharValue];
setting.CouplingYawCorrection=[[theDictionary objectForKey:kKeyCouplingYawCorrection ] unsignedCharValue];
setting.WinkelUmschlagNick=[[theDictionary objectForKey:kKeyWinkelUmschlagNick ] unsignedCharValue];
setting.WinkelUmschlagRoll=[[theDictionary objectForKey:kKeyWinkelUmschlagRoll ] unsignedCharValue];
setting.GyroAccAbgleich=[[theDictionary objectForKey:kKeyGyroAccAbgleich ] unsignedCharValue];
setting.Driftkomp=[[theDictionary objectForKey:kKeyDriftkomp ] unsignedCharValue];
setting.DynamicStability=[[theDictionary objectForKey:kKeyDynamicStability ] unsignedCharValue];
setting.UserParam5=[[theDictionary objectForKey:kKeyUserParam5 ] unsignedCharValue];
setting.UserParam6=[[theDictionary objectForKey:kKeyUserParam6 ] unsignedCharValue];
setting.UserParam7=[[theDictionary objectForKey:kKeyUserParam7 ] unsignedCharValue];
setting.UserParam8=[[theDictionary objectForKey:kKeyUserParam8 ] unsignedCharValue];
setting.J16Bitmask=[[theDictionary objectForKey:kKeyJ16Bitmask ] unsignedCharValue];
setting.J16Timing=[[theDictionary objectForKey:kKeyJ16Timing ] unsignedCharValue];
setting.J17Bitmask=[[theDictionary objectForKey:kKeyJ17Bitmask ] unsignedCharValue];
setting.J17Timing=[[theDictionary objectForKey:kKeyJ17Timing ] unsignedCharValue];
setting.WARN_J16_Bitmask=[[theDictionary objectForKey:kKeyWARN_J16_Bitmask ] unsignedCharValue];
setting.WARN_J17_Bitmask=[[theDictionary objectForKey:kKeyWARN_J17_Bitmask ] unsignedCharValue];
setting.NaviGpsModeControl=[[theDictionary objectForKey:kKeyNaviGpsModeControl ] unsignedCharValue];
setting.NaviGpsGain=[[theDictionary objectForKey:kKeyNaviGpsGain ] unsignedCharValue];
setting.NaviGpsP=[[theDictionary objectForKey:kKeyNaviGpsP ] unsignedCharValue];
setting.NaviGpsI=[[theDictionary objectForKey:kKeyNaviGpsI ] unsignedCharValue];
setting.NaviGpsD=[[theDictionary objectForKey:kKeyNaviGpsD ] unsignedCharValue];
setting.NaviGpsPLimit=[[theDictionary objectForKey:kKeyNaviGpsPLimit ] unsignedCharValue];
setting.NaviGpsILimit=[[theDictionary objectForKey:kKeyNaviGpsILimit ] unsignedCharValue];
setting.NaviGpsDLimit=[[theDictionary objectForKey:kKeyNaviGpsDLimit ] unsignedCharValue];
setting.NaviGpsACC=[[theDictionary objectForKey:kKeyNaviGpsACC ] unsignedCharValue];
setting.NaviGpsMinSat=[[theDictionary objectForKey:kKeyNaviGpsMinSat ] unsignedCharValue];
setting.NaviStickThreshold=[[theDictionary objectForKey:kKeyNaviStickThreshold ] unsignedCharValue];
setting.NaviWindCorrection=[[theDictionary objectForKey:kKeyNaviWindCorrection ] unsignedCharValue];
setting.NaviSpeedCompensation=[[theDictionary objectForKey:kKeyNaviSpeedCompensation ] unsignedCharValue];
setting.NaviOperatingRadius=[[theDictionary objectForKey:kKeyNaviOperatingRadius ] unsignedCharValue];
setting.NaviAngleLimitation=[[theDictionary objectForKey:kKeyNaviAngleLimitation ] unsignedCharValue];
setting.NaviPH_LoginTime=[[theDictionary objectForKey:kKeyNaviPH_LoginTime ] unsignedCharValue];
setting.ExternalControl=[[theDictionary objectForKey:kKeyExternalControl ] unsignedCharValue];
setting.BitConfig=0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_LOOP_UP ] boolValue]?CFG_LOOP_OBEN :0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_LOOP_DOWN ] boolValue]?CFG_LOOP_UNTEN :0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_LOOP_LEFT ] boolValue]?CFG_LOOP_LINKS :0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_LOOP_RIGHT ] boolValue]?CFG_LOOP_RECHTS :0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_MOTOR_BLINK ] boolValue]?CFG_MOTOR_BLINK :0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_MOTOR_OFF_LED1] boolValue]?CFG_MOTOR_OFF_LED1:0;
setting.BitConfig|= [[theDictionary objectForKey:kKeyBitConfig_MOTOR_OFF_LED2] boolValue]?CFG_MOTOR_OFF_LED2:0;
setting.ServoCompInvert=0;
setting.ServoCompInvert|= [[theDictionary objectForKey:kKeyServoCompInvert_Nick] boolValue]?CFG2_INVERT_NICK:0;
setting.ServoCompInvert|= [[theDictionary objectForKey:kKeyServoCompInvert_ROLL] boolValue]?CFG2_INVERT_ROLL:0;
setting.ExtraConfig=0;
setting.ExtraConfig|= [[theDictionary objectForKey:kKeyExtraConfig_HEIGHT_LIMIT] boolValue]?CFG2_HEIGHT_LIMIT:0;
setting.ExtraConfig|= [[theDictionary objectForKey:kKeyExtraConfig_VARIO_BEEP ] boolValue]?CFG2_VARIO_BEEP :0;
setting.ExtraConfig|= [[theDictionary objectForKey:kKeyExtraConfig_SENSITIVE_RC] boolValue]?CFG_SENSITIVE_RC :0;
NSString* name=[theDictionary objectForKey:kKeyName];
memset(setting.Name, 0, 12);
[name getBytes:(void*)setting.Name
maxLength:12
usedLength:NULL
encoding:NSASCIIStringEncoding
options:0
range:NSMakeRange(0,[name length])
remainingRange:NULL];
unsigned char payloadData[sizeof(MKSetting)+2];
payloadData[0]=[[theDictionary objectForKey:kMKDataKeyIndex] unsignedCharValue];
payloadData[1]=[[theDictionary objectForKey:kMKDataKeyVersion] unsignedCharValue];
memcpy((unsigned char *)(payloadData + 2),(unsigned char *)&setting,sizeof(MKSetting));
return [NSData dataWithBytes:payloadData length:sizeof(payloadData)];
}
 
@end
/iKopter/trunk/Classes/Communication/SynthesizeSingleton.h
0,0 → 1,68
//
// SynthesizeSingleton.h
// CocoaWithLove
//
// Created by Matt Gallagher on 20/10/08.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
 
#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
\
static classname *shared##classname = nil; \
\
+ (classname *)shared##classname \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [[self alloc] init]; \
} \
} \
\
return shared##classname; \
} \
\
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return self; \
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return NSUIntegerMax; \
} \
\
- (void)release \
{ \
} \
\
- (id)autorelease \
{ \
return self; \
}
/iKopter/trunk/Classes/Hosts/MKHost.h
0,0 → 1,40
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
 
 
@interface MKHost : NSObject<NSCoding, NSCopying> {
NSString* _name;
NSString* _address;
NSUInteger _port;
NSString* _connectionClass;
}
 
@property(retain) NSString* name;
@property(retain) NSString* address;
@property(assign) NSUInteger port;
@property(retain) NSString* connectionClass;
 
@end
/iKopter/trunk/Classes/Hosts/MKHost.m
0,0 → 1,90
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "MKHost.h"
 
#define kNameKey @"name"
#define kAddressKey @"address"
#define kPortKey @"port"
#define kConnectionClassKey @"connectionClass"
 
@implementation MKHost
 
@synthesize name=_name;
@synthesize address=_address;
@synthesize port=_port;
@synthesize connectionClass=_connectionClass;
 
- (id) init
{
self = [super init];
if (self != nil) {
self.name = NSLocalizedString(@"New host",@"Default host name");
}
return self;
}
 
- (void) dealloc
{
self.name=nil;
self.address=nil;
self.connectionClass=nil;
[super dealloc];
}
 
-(NSString*) description {
return self.name;
}
 
#pragma mark NSCoding
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:_name forKey:kNameKey];
[encoder encodeObject:_address forKey:kAddressKey];
[encoder encodeInteger:_port forKey:kPortKey];
[encoder encodeObject:_connectionClass forKey:kConnectionClassKey];
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:kNameKey];
self.address = [decoder decodeObjectForKey:kAddressKey];
self.port = [decoder decodeIntegerForKey:kPortKey];
self.connectionClass = [decoder decodeObjectForKey:kConnectionClassKey];
}
return self;
}
#pragma mark -
#pragma mark NSCopying
- (id)copyWithZone:(NSZone *)zone {
MKHost *copy = [[[self class] allocWithZone: zone] init];
copy.name = [[self.name copyWithZone:zone] autorelease];
copy.address = [[self.address copyWithZone:zone] autorelease];
copy.port = self.port;
copy.connectionClass = [[self.connectionClass copyWithZone:zone] autorelease];
return copy;
}
 
 
@end
/iKopter/trunk/Classes/Hosts/MKHostViewController.h
0,0 → 1,40
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
#import "InAppSettings.h"
 
@class MKHost;
 
@interface MKHostViewController : InAppSettingsViewController<InAppSettingsDatasource> {
MKHost* _host;
}
 
@property(nonatomic,retain) MKHost* host;
 
- (id)initWithHost:(MKHost*)theHost;
 
 
@end
/iKopter/trunk/Classes/Hosts/MKHostViewController.m
0,0 → 1,81
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "MKHostViewController.h"
 
 
@implementation MKHostViewController
 
@synthesize host=_host;
 
#pragma mark -
 
- (id)initWithHost:(MKHost*)theHost {
 
if ((self = [super initWithFile:@"MKHost"])) {
self.title = NSLocalizedString(@"MK Host",@"MK Host Title");
self.host = theHost;
super.dataSource = self;
}
return self;
}
 
- (void)dealloc {
self.host = nil;
[_host release];
[super dealloc];
}
 
#pragma mark -
 
// called after this controller's view will appear
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setToolbarHidden:YES animated:NO];
}
 
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
 
#pragma mark -
#pragma mark InAppSettingsDatasource
 
- (id) objectForKey:(id)aKey {
return [self.host valueForKey:aKey];
}
 
- (void) setObject:(id)anObject forKey:(id)aKey {
[self.host setValue:anObject forKey:aKey];
}
 
@end
 
/iKopter/trunk/Classes/Hosts/MKHosts.h
0,0 → 1,42
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <Foundation/Foundation.h>
 
@class MKHost;
 
@interface MKHosts : NSObject {
 
NSMutableArray* hosts;
}
 
-(NSUInteger) count;
 
-(MKHost*) hostAtIndexPath:(NSIndexPath *)indexPath;
 
-(NSIndexPath*) addHost;
-(void) moveHostAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath;
-(void) deleteHostAtIndexPath:(NSIndexPath*)indexPath;
 
@end
/iKopter/trunk/Classes/Hosts/MKHosts.m
0,0 → 1,175
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "MKHosts.h"
#import "MKHost.h"
 
#import "TTGlobalCorePaths.h"
#import "TTCorePreprocessorMacros.h"
 
@interface MKHosts (Private)
-(void) load;
-(void) save;
@end
 
@implementation MKHosts
 
- (id) init
{
self = [super init];
if (self != nil) {
[self load];
if(!hosts) {
hosts = [[NSMutableArray alloc]init];
MKHost* h;
h = [[MKHost alloc]init];
h.name = @"Quadkopter WLAN";
h.address = @"192.168.0.74";
h.port = 23;
h.connectionClass = @"MKIpConnection";
[hosts addObject:h];
[h release];
h = [[MKHost alloc]init];
h.name = @"Quadkopter QMK";
h.address = @"127.0.0.1";
h.port = 64400;
h.connectionClass = @"MKQmkIpConnection";
[hosts addObject:h];
[h release];
h = [[MKHost alloc]init];
h.name = @"Quadkopter Fake";
h.address = @"192.168.0.74";
h.port = 23;
h.connectionClass = @"MKFakeConnection";
[hosts addObject:h];
[h release];
 
 
[self save];
}
}
return self;
}
 
- (void) dealloc
{
[hosts release];
[super dealloc];
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
 
#define kFilename @"mkhosts"
#define kDataKey @"Data"
 
-(void) load {
NSString *filePath = TTPathForDocumentsResource(kFilename);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
DLog(@"Load the hosts from %@",filePath);
NSData *data = [[NSMutableData alloc]
initWithContentsOfFile:filePath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
TT_RELEASE_SAFELY(hosts);
hosts = [unarchiver decodeObjectForKey:kDataKey];
[hosts retain];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
 
DLog(@"Loaded the hosts %@",hosts);
 
}
}
 
-(void) save {
 
NSString *filePath = TTPathForDocumentsResource(kFilename);
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
 
[archiver encodeObject:hosts forKey:kDataKey];
[archiver finishEncoding];
[data writeToFile:filePath atomically:YES];
 
[archiver release];
[data release];
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////
 
-(NSUInteger) count {
return [hosts count];
}
 
-(MKHost*) hostAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
return [hosts objectAtIndex:row];
}
 
-(NSIndexPath*) addHost {
MKHost* h = [[MKHost alloc]init];
h.connectionClass = @"MKFakeConnection";
[hosts addObject:h];
[h release];
 
[self save];
return [NSIndexPath indexPathForRow:[hosts count]-1 inSection:0];
}
 
-(void) moveHostAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSLog(@"moveHostAtIndexPath %@ -> %@",fromIndexPath,toIndexPath);
NSLog(@"%@",hosts);
 
NSUInteger fromRow = [fromIndexPath row];
NSUInteger toRow = [toIndexPath row];
id object = [[hosts objectAtIndex:fromRow] retain];
[hosts removeObjectAtIndex:fromRow];
[hosts insertObject:object atIndex:toRow];
[object release];
NSLog(@"%@",hosts);
[self save];
}
 
-(void) deleteHostAtIndexPath:(NSIndexPath*)indexPath {
[hosts removeObjectAtIndex:[indexPath row]];
[self save];
}
 
@end
 
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSChildPaneSpecifierCell.h
0,0 → 1,14
//
// PSChildPaneSpecifierCell.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
 
@interface InAppSettingsPSChildPaneSpecifierCell : InAppSettingsTableCell {}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSChildPaneSpecifierCell.m
0,0 → 1,27
//
// PSToggleSwitchSpecifier.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsPSChildPaneSpecifierCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSChildPaneSpecifierCell
 
- (void)setUIValues{
[super setUIValues];
[self setTitle];
}
 
- (void)setupCell{
[super setupCell];
[self setDisclosure:YES];
self.canSelectCell = YES;
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSMultiValueSpecifierCell.h
0,0 → 1,16
//
// PSToggleSwitchSpecifier.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
 
@interface InAppSettingsPSMultiValueSpecifierCell : InAppSettingsTableCell {}
 
- (NSString *)getValueTitle;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSMultiValueSpecifierCell.m
0,0 → 1,41
//
// PSToggleSwitchSpecifier.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsPSMultiValueSpecifierCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSMultiValueSpecifierCell
 
- (NSString *)getValueTitle{
NSArray *titles = [self.setting valueForKey:InAppSettingsSpecifierTitles];
NSArray *values = [self.setting valueForKey:InAppSettingsSpecifierValues];
id obj = [self.setting getValue];
NSLog(@"%@",obj);
NSInteger valueIndex = [values indexOfObject:[self.setting getValue]];
if((valueIndex >= 0) && (valueIndex < (NSInteger)[titles count])){
return InAppSettingsLocalize([titles objectAtIndex:valueIndex], self.setting.stringsTable);
}
return nil;
}
 
- (void)setUIValues{
[super setUIValues];
[self setTitle];
[self setDetail:[self getValueTitle]];
}
 
- (void)setupCell{
[super setupCell];
[self setDisclosure:YES];
self.canSelectCell = YES;
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSSliderSpecifierCell.h
0,0 → 1,20
//
// PSToggleSwitchSpecifier.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
 
@interface InAppSettingsPSSliderSpecifierCell : InAppSettingsTableCell {
UISlider *valueSlider;
}
 
@property (nonatomic, retain) UISlider *valueSlider;
 
- (void)slideAction;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSSliderSpecifierCell.m
0,0 → 1,55
//
// PSToggleSwitchSpecifier.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsPSSliderSpecifierCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSSliderSpecifierCell
 
@synthesize valueSlider;
 
- (void)slideAction{
[self.setting setValue:[NSNumber numberWithFloat:[self.valueSlider value]]];
}
 
- (void)setUIValues{
[super setUIValues];
 
//get the abolute path to the images
NSString *minImagePath = [InAppSettingsBundlePath stringByAppendingPathComponent:[self.setting valueForKey:@"MinimumValueImage"]];
NSString *maxImagePath = [InAppSettingsBundlePath stringByAppendingPathComponent:[self.setting valueForKey:@"MaximumValueImage"]];
//setup the slider
self.valueSlider.minimumValue = [[self.setting valueForKey:InAppSettingsSpecifierMinimumValue] floatValue];
self.valueSlider.maximumValue = [[self.setting valueForKey:InAppSettingsSpecifierMaximumValue] floatValue];
self.valueSlider.minimumValueImage = [UIImage imageWithContentsOfFile:minImagePath];
self.valueSlider.maximumValueImage = [UIImage imageWithContentsOfFile:maxImagePath];
CGRect valueSliderFrame = self.valueSlider.frame;
valueSliderFrame.origin.y = (CGFloat)round((self.contentView.frame.size.height*0.5f)-(valueSliderFrame.size.height*0.5f));
valueSliderFrame.origin.x = InAppSettingsCellPadding;
valueSliderFrame.size.width = InAppSettingsScreenWidth-(InAppSettingsTotalTablePadding+InAppSettingsTotalCellPadding);
self.valueSlider.frame = valueSliderFrame;
self.valueSlider.value = [[self.setting getValue] floatValue];
}
 
- (void)setupCell{
[super setupCell];
//create the slider
self.valueSlider = [[UISlider alloc] initWithFrame:CGRectZero];
[self.valueSlider addTarget:self action:@selector(slideAction) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.valueSlider];
}
 
- (void)dealloc{
[valueSlider release];
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSTextFieldSpecifierCell.h
0,0 → 1,24
//
// PSToggleSwitchSpecifier.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
 
@interface InAppSettingsPSTextFieldSpecifierCell : InAppSettingsTableCell {
UITextField *textField;
}
 
@property (nonatomic, retain) UITextField *textField;
 
- (BOOL)isSecure;
- (UIKeyboardType)getKeyboardType;
- (UITextAutocapitalizationType)getAutocapitalizationType;
- (UITextAutocorrectionType)getAutocorrectionType;
- (void)textChangeAction;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSTextFieldSpecifierCell.m
0,0 → 1,137
//
// PSToggleSwitchSpecifier.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsPSTextFieldSpecifierCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSTextFieldSpecifierCell
 
@synthesize textField;
 
#pragma mark helper methods
 
- (BOOL)isSecure{
NSNumber *isSecure = [self.setting valueForKey:@"IsSecure"];
if(!isSecure){
return NO;
}
return [isSecure boolValue];
}
 
- (UIKeyboardType)getKeyboardType{
NSString *keyboardType = [self.setting valueForKey:@"KeyboardType"];
if([keyboardType isEqualToString:@"NumbersAndPunctuation"]){
return UIKeyboardTypeNumbersAndPunctuation;
}
else if([keyboardType isEqualToString:@"NumberPad"]){
return UIKeyboardTypeNumberPad;
}
else if([keyboardType isEqualToString:@"URL"]){
return UIKeyboardTypeURL;
}
else if([keyboardType isEqualToString:@"EmailAddress"]){
return UIKeyboardTypeEmailAddress;
}
return UIKeyboardTypeAlphabet;
}
 
- (UITextAutocapitalizationType)getAutocapitalizationType{
//this works, but the real settings don't seem to respect these values eventhough they are in the docs
NSString *autoCapitalizationType = [self.setting valueForKey:@"AutoCapitalizationType"];
if([autoCapitalizationType isEqualToString:@"Words"]){
return UITextAutocapitalizationTypeWords;
}
else if([autoCapitalizationType isEqualToString:@"Sentences"]){
return UITextAutocapitalizationTypeSentences;
}
else if([autoCapitalizationType isEqualToString:@"AllCharacters"]){
return UITextAutocapitalizationTypeAllCharacters;
}
return UITextAutocapitalizationTypeNone;
}
 
- (UITextAutocorrectionType)getAutocorrectionType{
NSString *autocorrectionType = [self.setting valueForKey:@"AutocorrectionType"];
if([autocorrectionType isEqualToString:@"Yes"]){
return UITextAutocorrectionTypeYes;
}
else if([autocorrectionType isEqualToString:@"No"]){
return UITextAutocorrectionTypeNo;
}
return UITextAutocorrectionTypeDefault;
}
 
- (void)textChangeAction{
NSNumber* isNumtype = [self.setting valueForKey:InAppSettingsSpecifierInAppNumtype];
if ( [isNumtype boolValue] ){
NSNumber* n = [NSNumber numberWithInt:[self.textField.text intValue]];
[self.setting setValue:n];
}
else
[self.setting setValue:self.textField.text];
}
 
#pragma mark cell controlls
 
- (void)setValueDelegate:(id)delegate{
self.textField.delegate = delegate;
[super setValueDelegate:delegate];
}
 
- (void)setUIValues{
[super setUIValues];
[self setTitle];
CGRect textFieldFrame = self.textField.frame;
CGSize titleSize = [titleLabel.text sizeWithFont:titleLabel.font];
textFieldFrame.origin.x = (CGFloat)round(titleSize.width+InAppSettingsTotalTablePadding);
if(textFieldFrame.origin.x < InAppSettingsCellTextFieldMinX){
textFieldFrame.origin.x = InAppSettingsCellTextFieldMinX;
}
textFieldFrame.origin.y = (CGFloat)round((self.contentView.frame.size.height*0.5f)-(titleSize.height*0.5f))-InAppSettingsOffsetY;
textFieldFrame.size.width = (CGFloat)round((InAppSettingsScreenWidth-(InAppSettingsTotalTablePadding+InAppSettingsCellPadding))-textFieldFrame.origin.x);
textFieldFrame.size.height = titleSize.height;
self.textField.frame = textFieldFrame;
self.textField.text = [[self.setting getValue] description];
//keyboard traits
self.textField.secureTextEntry = [self isSecure];
self.textField.keyboardType = [self getKeyboardType];
self.textField.autocapitalizationType = [self getAutocapitalizationType];
self.textField.autocorrectionType = [self getAutocorrectionType];
//these are set here so they are set per cell
//self.textField.delegate = self;
self.valueInput = self.textField;
}
 
- (void)setupCell{
[super setupCell];
//create text field
self.textField =[[UITextField alloc] initWithFrame:CGRectZero];
self.textField.textColor = InAppSettingsBlue;
self.textField.adjustsFontSizeToFitWidth = YES;
//THIS IS NOT THE BEHAVIOR OF THE SETTINGS APP
//but we need a way to dismiss the keyboard
self.textField.returnKeyType = UIReturnKeyDone;
[self.textField addTarget:self action:@selector(textChangeAction) forControlEvents:UIControlEventEditingChanged];
[self.contentView addSubview:self.textField];
}
 
- (void)dealloc{
[textField release];
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSTitleValueSpecifierCell.h
0,0 → 1,16
//
// PSTitleValueSpecifierCell.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
 
@interface InAppSettingsPSTitleValueSpecifierCell : InAppSettingsTableCell {}
 
- (NSString *)getValueTitle;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSTitleValueSpecifierCell.m
0,0 → 1,45
//
// PSTitleValueSpecifierCell.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsPSTitleValueSpecifierCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSTitleValueSpecifierCell
 
- (NSString *)getValueTitle{
NSArray *titles = [self.setting valueForKey:InAppSettingsSpecifierTitles];
NSArray *values = [self.setting valueForKey:InAppSettingsSpecifierValues];
if(titles || values){
if(([titles count] == 0) || ([values count] == 0) || ([titles count] != [values count])){
return nil;
}
NSInteger valueIndex = [values indexOfObject:[self.setting getValue]];
if((valueIndex >= 0) && (valueIndex < (NSInteger)[titles count])){
return [titles objectAtIndex:valueIndex];
}
return nil;
}
return [self.setting getValue];
}
 
- (void)setUIValues{
[super setUIValues];
[self setTitle];
if([self.setting valueForKey:InAppSettingsSpecifierInAppURL]){
[self setDisclosure:YES];
self.canSelectCell = YES;
}
[self setDetail:[self getValueTitle]];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSToggleSwitchSpecifierCell.h
0,0 → 1,22
//
// PSToggleSwitchSpecifier.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
 
@interface InAppSettingsPSToggleSwitchSpecifierCell : InAppSettingsTableCell {
UISwitch *valueSwitch;
}
 
@property (nonatomic, retain) UISwitch *valueSwitch;
 
- (BOOL)getBool;
- (void)setBool:(BOOL)newValue;
- (void)switchAction;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPSToggleSwitchSpecifierCell.m
0,0 → 1,84
//
// PSToggleSwitchSpecifier.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsPSToggleSwitchSpecifierCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSToggleSwitchSpecifierCell
 
@synthesize valueSwitch;
 
// The value associated with the preference when the toggle switch
// is in the ON position. The value type for this key can be any
// scalar type, including Boolean, String, Number, Date, or Data.
// If this key is not present, the default value type is a Boolean.
 
- (BOOL)getBool{
id value = [self.setting getValue];
id trueValue = [self.setting valueForKey:@"TrueValue"];
id falseValue = [self.setting valueForKey:@"FalseValue"];
if([value isEqual:trueValue]){
return YES;
}
if([value isEqual:falseValue]){
return NO;
}
//if there is no true or false values the value has to be a bool
return [value boolValue];
}
 
- (void)setBool:(BOOL)newValue{
id value = [NSNumber numberWithBool:newValue];
if(newValue){
id trueValue = [self.setting valueForKey:@"TrueValue"];
if(trueValue){
value = trueValue;
}
}
else{
id falseValue = [self.setting valueForKey:@"FalseValue"];
if(falseValue){
value = falseValue;
}
}
[self.setting setValue:value];
}
 
- (void)switchAction{
[self setBool:[self.valueSwitch isOn]];
}
 
- (void)setUIValues{
[super setUIValues];
[self setTitle];
self.valueSwitch.on = [self getBool];
}
 
- (void)setupCell{
[super setupCell];
//create the switch
self.valueSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
CGRect valueSwitchFrame = self.valueSwitch.frame;
valueSwitchFrame.origin.y = (CGFloat)round((self.contentView.frame.size.height*0.5f)-(valueSwitchFrame.size.height*0.5f))-InAppSettingsOffsetY;
valueSwitchFrame.origin.x = (CGFloat)round((InAppSettingsScreenWidth-(InAppSettingsTotalTablePadding+InAppSettingsCellPadding))-valueSwitchFrame.size.width);
self.valueSwitch.frame = valueSwitchFrame;
[self.valueSwitch addTarget:self action:@selector(switchAction) forControlEvents:UIControlEventValueChanged];
[self.contentView addSubview:self.valueSwitch];
}
 
- (void)dealloc{
[valueSwitch release];
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPotiValueSpecifierCell.h
0,0 → 1,16
//
// InAppSettingsPotiValueSpecifierCell.h
// InAppSettingsTestApp
//
// Created by Frank Blumenberg on 04.05.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
 
#import <Foundation/Foundation.h>
#import "InAppSettings.h"
 
@interface InAppSettingsPotiValueSpecifierCell : InAppSettingsTableCell {}
 
- (NSString *)getValueTitle;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsPotiValueSpecifierCell.m
0,0 → 1,40
//
// InAppSettingsPotiValueSpecifierCell.m
// InAppSettingsTestApp
//
// Created by Frank Blumenberg on 04.05.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
 
#import "InAppSettingsPotiValueSpecifierCell.h"
 
 
@implementation InAppSettingsPotiValueSpecifierCell
 
- (NSString *)getValueTitle{
NSNumber* value = [self.setting getValue];
int intValue=[value intValue];
if( intValue <= 245 )
return [NSString stringWithFormat:@"%@",value];
return [NSString stringWithFormat:NSLocalizedString(@"Poti%d",@"Potiname"),256-intValue];
}
 
- (void)setUIValues{
[super setUIValues];
[self setTitle];
[self setDetail:[self getValueTitle]];
}
 
- (void)setupCell{
[super setupCell];
[self setDisclosure:YES];
self.canSelectCell = YES;
}
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsTableCell.h
0,0 → 1,35
//
// InAppSettingsTableCell.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsSpecifier.h"
 
@interface InAppSettingsTableCell : UITableViewCell {
InAppSettingsSpecifier *setting;
UILabel *titleLabel, *valueLabel;
UIControl *valueInput;
BOOL canSelectCell;
}
 
@property (nonatomic, retain) InAppSettingsSpecifier *setting;
@property (nonatomic, retain) UILabel *titleLabel, *valueLabel;
@property (nonatomic, assign) UIControl *valueInput;
@property (nonatomic, assign) BOOL canSelectCell;
 
- (void)setTitle;
- (void)setDetail;
- (void)setDetail:(NSString *)detail;
- (void)setDisclosure:(BOOL)disclosure;
 
- (void)setValueDelegate:(id)delegate;
 
- (void)setupCell;
- (void)setUIValues;
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier;
 
@end
/iKopter/trunk/Classes/InAppSettings/Cells/InAppSettingsTableCell.m
0,0 → 1,155
//
// InAppSettingsTableCell.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsTableCell.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsTableCell
 
@synthesize setting;
@synthesize titleLabel, valueLabel;
@synthesize valueInput;
@synthesize canSelectCell;
 
#pragma mark Cell lables
 
- (void)setTitle{
self.titleLabel.text = [self.setting localizedTitle];
CGSize titleSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font];
CGFloat maxTitleWidth = InAppSettingsCellTitleMaxWidth;
if([self.setting isType:InAppSettingsPSToggleSwitchSpecifier]){
maxTitleWidth = InAppSettingsCellTitleMaxWidth-(InAppSettingsCellToggleSwitchWidth+InAppSettingsCellPadding);
}else if(self.accessoryType == UITableViewCellAccessoryDisclosureIndicator){
maxTitleWidth = InAppSettingsCellTitleMaxWidth-(InAppSettingsCellDisclosureIndicatorWidth+InAppSettingsCellPadding);
}
if(titleSize.width > maxTitleWidth){
titleSize.width = maxTitleWidth;
}
 
CGRect titleFrame = self.titleLabel.frame;
titleFrame.size = titleSize;
titleFrame.origin.x = InAppSettingsCellPadding;
titleFrame.origin.y = (CGFloat)round((self.contentView.frame.size.height*0.5f)-(titleSize.height*0.5f))-InAppSettingsOffsetY;
self.titleLabel.frame = titleFrame;
}
 
- (void)setDetail{
[self setDetail:[self.setting getValue]];
}
 
- (void)setDetail:(NSString *)detail{
//the detail is not localized
self.valueLabel.text = detail;
 
CGSize valueSize = [self.valueLabel.text sizeWithFont:self.valueLabel.font];
CGRect valueFrame = self.valueLabel.frame;
CGFloat titleRightSide = self.titleLabel.frame.size.width+InAppSettingsTablePadding;
if([self.setting isType:InAppSettingsPSMultiValueSpecifier] && [[self.setting localizedTitle] length] == 0){
valueFrame.origin.x = InAppSettingsCellPadding;
}else{
valueFrame.origin.x = (InAppSettingsScreenWidth-(InAppSettingsTotalTablePadding+InAppSettingsCellPadding))-valueSize.width;
if(self.accessoryType == UITableViewCellAccessoryDisclosureIndicator){
valueFrame.origin.x -= InAppSettingsCellDisclosureIndicatorWidth+InAppSettingsCellPadding;
}
if(titleRightSide >= valueFrame.origin.x){
valueFrame.origin.x = titleRightSide;
}
}
valueFrame.origin.y = (CGFloat)round((self.contentView.frame.size.height*0.5f)-(valueSize.height*0.5f))-InAppSettingsOffsetY;
valueFrame.size.width = InAppSettingsScreenWidth-(valueFrame.origin.x+InAppSettingsTotalTablePadding+InAppSettingsCellPadding);
if(self.accessoryType == UITableViewCellAccessoryDisclosureIndicator){
valueFrame.size.width -= InAppSettingsCellDisclosureIndicatorWidth+InAppSettingsCellPadding;
}
//if the width is less then 0 just hide the label
if(valueFrame.size.width <= 0){
self.valueLabel.hidden = YES;
}else{
self.valueLabel.hidden = NO;
}
valueFrame.size.height = valueSize.height;
self.valueLabel.frame = valueFrame;
}
 
- (void)setDisclosure:(BOOL)disclosure{
if(disclosure){
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else{
self.accessoryType = UITableViewCellAccessoryNone;
}
}
 
- (void)setCanSelectCell:(BOOL)value{
if(value){
self.selectionStyle = UITableViewCellSelectionStyleBlue;
}else{
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
canSelectCell = value;
}
 
#pragma mark -
 
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier{
//the docs say UITableViewCellStyleValue1 is used for settings,
//but it doesn't look 100% the same so we will just draw our own UILabels
#if InAppSettingsUseNewCells
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
#else
self = [super initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier];
#endif
if(self != nil){
self.canSelectCell = NO;
}
return self;
}
 
#pragma mark implement in cell
 
- (void)setUIValues{
//implement this per cell type
}
 
- (void)setValueDelegate:(id)delegate{
//implement in cell
}
 
- (void)setupCell{
//setup title label
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.titleLabel.font = InAppSettingsBoldFont;
self.titleLabel.highlightedTextColor = [UIColor whiteColor];
self.titleLabel.backgroundColor = [UIColor clearColor];
// self.titleLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.titleLabel];
//setup value label
self.valueLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.valueLabel.font = InAppSettingsNormalFont;
self.valueLabel.textColor = InAppSettingsBlue;
self.valueLabel.highlightedTextColor = [UIColor whiteColor];
self.valueLabel.backgroundColor = [UIColor clearColor];
// self.valueLabel.backgroundColor = [UIColor redColor];
[self.contentView addSubview:self.valueLabel];
}
 
- (void)dealloc{
[setting release];
[titleLabel release];
[valueLabel release];
self.valueInput = nil;
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettings.h
0,0 → 1,83
//
// InAppSettingsViewController.h
// InAppSettings
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsTableCell.h"
#import "InAppSettingsReader.h"
#import "InAppSettingsSpecifier.h"
 
@protocol InAppSettingsDelegate;
 
@protocol InAppSettingsDatasource < NSObject >
- (id) objectForKey:(id)aKey;
- (void) setObject:(id)anObject forKey:(id)aKey;
@end
 
@interface InAppSettings : NSObject {}
 
+ (void) registerDefaults;
 
@end
 
@interface InAppSettingsModalViewController : UIViewController {}
 
@end
 
@interface InAppSettingsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, InAppSettingsSpecifierDelegate> {
NSString * file;
UITableView * settingsTableView;
UIControl * firstResponder;
InAppSettingsReader * settingsReader;
id<InAppSettingsDelegate> delegate;
id<InAppSettingsDatasource> dataSource;
}
 
@property (nonatomic, copy) NSString * file;
@property (nonatomic, retain) UITableView * settingsTableView;
@property (nonatomic, assign) UIControl * firstResponder;
@property (nonatomic, retain) InAppSettingsReader * settingsReader;
@property (assign) id<InAppSettingsDelegate> delegate;
@property (assign) id<InAppSettingsDatasource> dataSource;
 
- (id) initWithFile:(NSString *)inputFile;
 
// modal view
- (void) dismissModalView;
- (void) addDoneButton;
 
// keyboard notification
- (void) registerForKeyboardNotifications;
- (void) keyboardWillShow:(NSNotification *)notification;
- (void) keyboardWillHide:(NSNotification *)notification;
 
@end
 
@interface InAppSettingsLightningBolt : UIView {
BOOL flip;
}
 
@property (nonatomic, assign) BOOL flip;
 
@end
 
@protocol InAppSettingsDelegate < NSObject >
 
@optional
- (void) InAppSettingsValue:(id)value forKey:(NSString *)key;
 
@end
 
@protocol InAppSettingsChildPane < NSObject >
 
@property (nonatomic, retain) InAppSettingsSpecifier * setting;
 
- (id) initWithSetting:(InAppSettingsSpecifier *)inputSetting;
- (id) getValue;
- (void) setValue:(id)newValue;
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettings.m
0,0 → 1,375
//
// InAppSettingsViewController.m
// InAppSettings
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettings.h"
#import "InAppSettingsConstants.h"
#import "InAppSettingsPSMultiValueSpecifierTable.h"
 
@implementation InAppSettings
 
+ (void) registerDefaults {
[[[InAppSettingsReaderRegisterDefaults alloc] init] release];
}
 
@end
 
@implementation InAppSettingsModalViewController
 
- (id) init {
InAppSettingsViewController * settings = [[InAppSettingsViewController alloc] init];
 
self = [[UINavigationController alloc] initWithRootViewController:settings];
[settings addDoneButton];
[settings release];
return self;
}
 
@end
 
@implementation InAppSettingsViewController
 
@synthesize file;
@synthesize settingsTableView;
@synthesize firstResponder;
@synthesize settingsReader;
@synthesize delegate;
@synthesize dataSource;
 
 
#pragma mark modal view
 
- (void) dismissModalView {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
 
- (void) addDoneButton {
UIBarButtonItem * doneButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(dismissModalView)];
 
self.navigationItem.rightBarButtonItem = doneButton;
[doneButton release];
}
 
#pragma mark setup view
 
- (id) initWithFile:(NSString *)inputFile {
self = [super init];
if (self != nil) {
self.file = inputFile;
}
return self;
}
 
- (void) viewDidLoad {
// setup the table
self.settingsTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
self.settingsTableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.settingsTableView.delegate = self;
self.settingsTableView.dataSource = self;
[self.view addSubview:self.settingsTableView];
 
// if the title is nil set it to Settings
if (!self.title) {
self.title = NSLocalizedString(@"Settings", nil);
}
 
// load settigns plist
if (!self.file) {
self.file = InAppSettingsRootFile;
}
 
InAppSettingsReader* tmpSettingsReader = [[InAppSettingsReader alloc] initWithFile:self.file dataSource:dataSource];
self.settingsReader = tmpSettingsReader;
[tmpSettingsReader release];
 
// setup keyboard notification
self.firstResponder = nil;
[self registerForKeyboardNotifications];
}
 
- (void) viewWillAppear:(BOOL)animated {
self.firstResponder = nil;
 
self.settingsTableView.contentInset = UIEdgeInsetsZero;
self.settingsTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
 
[self.settingsTableView reloadData];
[super viewWillAppear:animated];
}
 
- (void) viewWillDisappear:(BOOL)animated {
self.firstResponder = nil;
[super viewWillDisappear:animated];
}
 
- (void) dealloc {
self.firstResponder = nil;
self.delegate = nil;
 
[file release];
[settingsTableView release];
[settingsReader release];
[super dealloc];
}
 
#pragma mark text field cell delegate
 
- (void) textFieldDidBeginEditing:(UITextField *)cellTextField {
self.firstResponder = cellTextField;
 
// TODO: find a better way to get the cell from the text view
NSIndexPath * indexPath = [self.settingsTableView indexPathForCell:(UITableViewCell *)[[cellTextField superview] superview]];
[self.settingsTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
 
- (BOOL) textFieldShouldReturn:(UITextField *)cellTextField {
self.firstResponder = nil;
[cellTextField resignFirstResponder];
return YES;
}
 
#pragma mark keyboard notification
 
- (void) registerForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
 
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil];
}
 
- (void) keyboardWillShow:(NSNotification *)notification {
if (self.firstResponder == nil) {
// get the keybaord rect
CGRect keyboardRect = [[[notification userInfo] objectForKey:UIKeyboardBoundsUserInfoKey] CGRectValue];
 
// determin the bottom inset for the table view
UIEdgeInsets settingsTableInset = self.settingsTableView.contentInset;
CGPoint tableViewScreenSpace = [self.settingsTableView.superview convertPoint:self.settingsTableView.frame.origin toView:nil];
CGFloat tableViewBottomOffset = InAppSettingsScreenHeight - (tableViewScreenSpace.y + self.settingsTableView.frame.size.height);
settingsTableInset.bottom = keyboardRect.size.height - tableViewBottomOffset;
 
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:InAppSettingsKeyboardAnimation];
[UIView setAnimationBeginsFromCurrentState:YES];
self.settingsTableView.contentInset = settingsTableInset;
self.settingsTableView.scrollIndicatorInsets = settingsTableInset;
[UIView commitAnimations];
}
}
 
- (void) keyboardWillHide:(NSNotification *)notification {
if (self.firstResponder == nil) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:InAppSettingsKeyboardAnimation];
[UIView setAnimationBeginsFromCurrentState:YES];
self.settingsTableView.contentInset = UIEdgeInsetsZero;
self.settingsTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
[UIView commitAnimations];
}
}
 
#pragma mark specifier delegate
 
- (void) settingsSpecifierUpdated:(InAppSettingsSpecifier *)specifier {
if ([self.delegate respondsToSelector:@selector(InAppSettingsValue:forKey:)]) {
[self.delegate InAppSettingsValue:[specifier getValue] forKey:[specifier getKey]];
}
}
 
#pragma mark Table view methods
 
- (InAppSettingsSpecifier *) settingAtIndexPath:(NSIndexPath *)indexPath {
return [[self.settingsReader.settings objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
}
 
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return [self.settingsReader.headers count];
}
 
- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.settingsReader.headers objectAtIndex:section];
}
 
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.settingsReader.settings objectAtIndex:section] count];
}
 
- (CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
if (InAppSettingsDisplayPowered && [self.file isEqualToString:InAppSettingsRootFile] && section == (NSInteger)[self.settingsReader.headers count] - 1) {
return InAppSettingsPowerFooterHeight;
}
return 0.0f;
}
 
- (UIView *) tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
if (InAppSettingsDisplayPowered && [self.file isEqualToString:InAppSettingsRootFile] && section == (NSInteger)[self.settingsReader.headers count] - 1) {
UIView * powerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, InAppSettingsScreenWidth, InAppSettingsPowerFooterHeight)];
 
// InAppSettings label
CGSize InAppSettingsSize = [InAppSettingsProjectName sizeWithFont:InAppSettingsFooterFont];
CGPoint InAppSettingsPos = CGPointMake((CGFloat)round((InAppSettingsScreenWidth * 0.5f) - (InAppSettingsSize.width * 0.5f)),
(CGFloat)round((InAppSettingsPowerFooterHeight * 0.5f) - (InAppSettingsSize.height * 0.5f)) - 1);
UILabel * InAppLabel = [[UILabel alloc] initWithFrame:CGRectMake(InAppSettingsPos.x, InAppSettingsPos.y, InAppSettingsSize.width, InAppSettingsSize.height)];
InAppLabel.text = InAppSettingsProjectName;
InAppLabel.font = InAppSettingsFooterFont;
InAppLabel.backgroundColor = [UIColor clearColor];
InAppLabel.textColor = InAppSettingsFooterBlue;
InAppLabel.shadowColor = [UIColor whiteColor];
InAppLabel.shadowOffset = CGSizeMake(0.0f, 1.0f);
[powerView addSubview:InAppLabel];
[InAppLabel release];
 
// lighting bolts
CGPoint leftLightningBoltPos = CGPointMake(InAppSettingsPos.x - InAppSettingsLightingBoltSize,
(CGFloat)round((InAppSettingsPowerFooterHeight * 0.5f) - (InAppSettingsLightingBoltSize * 0.5f)));
InAppSettingsLightningBolt * leftLightningBolt = [[InAppSettingsLightningBolt alloc]
initWithFrame:CGRectMake(leftLightningBoltPos.x, leftLightningBoltPos.y,
InAppSettingsLightingBoltSize, InAppSettingsLightingBoltSize)];
[powerView addSubview:leftLightningBolt];
[leftLightningBolt release];
 
CGPoint rightLightningBoltPos = CGPointMake((CGFloat)round(InAppSettingsPos.x + InAppSettingsSize.width), leftLightningBoltPos.y);
InAppSettingsLightningBolt * rightLightningBolt = [[InAppSettingsLightningBolt alloc]
initWithFrame:CGRectMake(rightLightningBoltPos.x, rightLightningBoltPos.y,
InAppSettingsLightingBoltSize, InAppSettingsLightingBoltSize)];
rightLightningBolt.flip = YES;
[powerView addSubview:rightLightningBolt];
[rightLightningBolt release];
 
return [powerView autorelease];
}
return nil;
}
 
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
InAppSettingsSpecifier * setting = [self settingAtIndexPath:indexPath];
 
setting.delegate = self;
 
// get the NSClass for a specifier, if there is none use the base class InAppSettingsTableCell
NSString * cellType = [setting cellName];
Class nsclass = NSClassFromString(cellType);
if (!nsclass) {
cellType = @"InAppSettingsTableCell";
nsclass = NSClassFromString(cellType);
}
 
InAppSettingsTableCell * cell = ((InAppSettingsTableCell *)[tableView dequeueReusableCellWithIdentifier:cellType]);
if (cell == nil) {
cell = [[[nsclass alloc] initWithReuseIdentifier:cellType] autorelease];
// setup the cells controlls
[cell setupCell];
}
 
// set the values of the cell, this is separated from setupCell for reloading the table
cell.setting = setting;
[cell setValueDelegate:self];
[cell setUIValues];
 
return cell;
}
 
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
InAppSettingsSpecifier * setting = [self settingAtIndexPath:indexPath];
 
if ([setting isType:InAppSettingsPSMultiValueSpecifier]) {
InAppSettingsPSMultiValueSpecifierTable * multiValueSpecifier = [[InAppSettingsPSMultiValueSpecifierTable alloc] initWithSetting:setting];
[self.navigationController pushViewController:multiValueSpecifier animated:YES];
[multiValueSpecifier release];
} else if ([setting isType:InAppSettingsPSChildPaneSpecifier]) {
 
if ( [setting valueForKey:InAppSettingsSpecifierInAppChildPaneClass] ) {
 
UIViewController * childPane;
 
Class childPaneClass = NSClassFromString([setting valueForKey:InAppSettingsSpecifierInAppChildPaneClass]);
if ( [childPaneClass conformsToProtocol:@protocol(InAppSettingsChildPane)] ) {
childPane = [[childPaneClass alloc] initWithSetting:setting];
} else {
childPane = [[childPaneClass alloc] init];
}
 
childPane.title = [setting localizedTitle];
[self.navigationController pushViewController:childPane animated:YES];
[childPane release];
} else {
 
InAppSettingsViewController * childPane = [[InAppSettingsViewController alloc] initWithFile:[setting valueForKey:InAppSettingsSpecifierFile]];
childPane.title = [setting localizedTitle];
childPane.dataSource = self.dataSource;
[self.navigationController pushViewController:childPane animated:YES];
[childPane release];
}
} else if ([setting isType:InAppSettingsPSTitleValueSpecifier]) {
InAppSettingsOpenUrl([NSURL URLWithString:[setting valueForKey:InAppSettingsSpecifierInAppURL]]);
}
}
 
- (NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
InAppSettingsTableCell * cell = ((InAppSettingsTableCell *)[tableView cellForRowAtIndexPath:indexPath]);
 
if ([cell.setting isType:@"PSTextFieldSpecifier"]) {
[cell.valueInput becomeFirstResponder];
} else if (cell.canSelectCell) {
[self.firstResponder resignFirstResponder];
return indexPath;
}
return nil;
}
 
@end
 
@implementation InAppSettingsLightningBolt
 
@synthesize flip;
 
- (id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self != nil) {
self.flip = NO;
self.backgroundColor = [UIColor clearColor];
}
return self;
}
 
- (void) drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
 
CGContextSetFillColorWithColor(context, [InAppSettingsFooterBlue CGColor]);
#if __IPHONE_3_2
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 0.0f, [[UIColor whiteColor] CGColor]);
#else
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, -1.0f), 0.0f, [[UIColor whiteColor] CGColor]);
#endif
if (self.flip) {
CGContextMoveToPoint(context, 4.0f, 1.0f);
CGContextAddLineToPoint(context, 13.0f, 1.0f);
CGContextAddLineToPoint(context, 10.0f, 5.0f);
CGContextAddLineToPoint(context, 12.0f, 7.0f);
CGContextAddLineToPoint(context, 2.0f, 15.0f);
CGContextAddLineToPoint(context, 5.0f, 7.0f);
CGContextAddLineToPoint(context, 3.0f, 5.0f);
} else {
CGContextMoveToPoint(context, 3.0f, 1.0f);
CGContextAddLineToPoint(context, 12.0f, 1.0f);
CGContextAddLineToPoint(context, 13.0f, 5.0f);
CGContextAddLineToPoint(context, 11.0f, 7.0f);
CGContextAddLineToPoint(context, 14.0f, 15.0f);
CGContextAddLineToPoint(context, 4.0f, 7.0f);
CGContextAddLineToPoint(context, 6.0f, 5.0f);
}
CGContextFillPath(context);
}
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsConstants.h
0,0 → 1,76
//
// InAppSettingsConstants.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <Availability.h>
 
#define InAppSettingsRootFile @"Root"
#define InAppSettingsProjectName @"InAppSettings"
 
#define InAppSettingsOffsetY 2.0f
#define InAppSettingsFontSize 17.0f
#define InAppSettingsCellPadding 9.0f
#define InAppSettingsTablePadding 10.0f
#define InAppSettingsPowerFooterHeight 32.0f
#define InAppSettingsLightingBoltSize 16.0f
#define InAppSettingsKeyboardAnimation 0.3f
#define InAppSettingsCellTextFieldMinX 115.0f
#define InAppSettingsCellToggleSwitchWidth 94.0f
#define InAppSettingsCellDisclosureIndicatorWidth 10.0f
#define InAppSettingsTotalCellPadding InAppSettingsCellPadding * 2
#define InAppSettingsTotalTablePadding InAppSettingsTablePadding * 2
#define InAppSettingsScreenWidth 320
#define InAppSettingsScreenHeight 480
#define InAppSettingsCellTitleMaxWidth InAppSettingsScreenWidth - (InAppSettingsTotalTablePadding + InAppSettingsTotalCellPadding)
#define InAppSettingsFooterFont [UIFont systemFontOfSize:14.0f]
#define InAppSettingsBoldFont [UIFont boldSystemFontOfSize:InAppSettingsFontSize]
#define InAppSettingsNormalFont [UIFont systemFontOfSize:InAppSettingsFontSize]
#define InAppSettingsBlue [UIColor colorWithRed:0.22f green:0.33f blue:0.53f alpha:1.0f];
#define InAppSettingsFooterBlue [UIColor colorWithRed:0.36f green:0.39f blue:0.45f alpha:1.0f]
 
#define InAppSettingsOpenUrl(url) [[UIApplication sharedApplication] openURL : url];
#define InAppSettingsBundlePath [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"]
#define InAppSettingsFullPlistPath(file) \
[InAppSettingsBundlePath stringByAppendingPathComponent :[file stringByAppendingPathExtension:@"plist"]]
#define InAppSettingsLocalize(stringKey, tableKey) \
[[NSBundle bundleWithPath:InAppSettingsBundlePath] localizedStringForKey : stringKey value : stringKey table : tableKey]
 
// settings strings
#define InAppSettingsStringsTable @"StringsTable"
#define InAppSettingsPreferenceSpecifiers @"PreferenceSpecifiers"
 
#define InAppSettingsPSGroupSpecifier @"PSGroupSpecifier"
#define InAppSettingsPSSliderSpecifier @"PSSliderSpecifier"
#define InAppSettingsPSChildPaneSpecifier @"PSChildPaneSpecifier"
#define InAppSettingsPSTextFieldSpecifier @"PSTextFieldSpecifier"
#define InAppSettingsPSTitleValueSpecifier @"PSTitleValueSpecifier"
#define InAppSettingsPSMultiValueSpecifier @"PSMultiValueSpecifier"
#define InAppSettingsPSToggleSwitchSpecifier @"PSToggleSwitchSpecifier"
 
#define InAppSettingsSpecifierKey @"Key"
#define InAppSettingsSpecifierType @"Type"
#define InAppSettingsSpecifierFile @"File"
#define InAppSettingsSpecifierTitle @"Title"
#define InAppSettingsSpecifierTitles @"Titles"
#define InAppSettingsSpecifierValues @"Values"
#define InAppSettingsSpecifierDefaultValue @"DefaultValue"
#define InAppSettingsSpecifierMinimumValue @"MinimumValue"
#define InAppSettingsSpecifierMaximumValue @"MaximumValue"
#define InAppSettingsSpecifierInAppURL @"InAppURL"
#define InAppSettingsSpecifierInAppTitle @"InAppTitle"
 
#define InAppSettingsSpecifierInAppNumtype @"InAppNumtype"
 
#define InAppSettingsSpecifierInAppChildPaneClass @"InAppChildPaneClass"
#define InAppSettingsSpecifierInAppCellClass @"InAppCellClass"
 
 
// test what cell init code should be used
#define InAppSettingsUseNewCells __IPHONE_3_0 && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_0
 
// if you dont want to display the footer set this to NO
#define InAppSettingsDisplayPowered YES
/iKopter/trunk/Classes/InAppSettings/InAppSettingsPSMultiValueSpecifierTable.h
0,0 → 1,22
//
// PSMultiValueSpecifierTable.h
// InAppSettings
//
// Created by David Keegan on 11/3/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettingsSpecifier.h"
 
@interface InAppSettingsPSMultiValueSpecifierTable : UITableViewController {
InAppSettingsSpecifier * setting;
}
 
@property (nonatomic, retain) InAppSettingsSpecifier * setting;
 
- (id) initWithSetting:(InAppSettingsSpecifier *)inputSetting;
- (id) getValue;
- (void) setValue:(id)newValue;
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsPSMultiValueSpecifierTable.m
0,0 → 1,126
//
// PSMultiValueSpecifierTable.m
// InAppSettings
//
// Created by David Keegan on 11/3/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettings.h"
#import "InAppSettingsPSMultiValueSpecifierTable.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsPSMultiValueSpecifierTable
 
@synthesize setting;
 
- (id) initWithStyle:(UITableViewStyle)style {
return [super initWithStyle:UITableViewStyleGrouped];
}
 
- (id) initWithSetting:(InAppSettingsSpecifier *)inputSetting {
self = [super init];
if (self != nil) {
self.setting = inputSetting;
}
return self;
}
 
- (void) viewDidLoad {
[super viewDidLoad];
 
self.title = [self.setting localizedTitle];
}
 
- (void) dealloc {
[setting release];
[super dealloc];
}
 
#pragma mark Value
 
- (id) getValue {
 
id value;
 
if ( self.setting.dataSource != nil )
value = [self.setting.dataSource objectForKey:[self.setting getKey]];
else
value = [[NSUserDefaults standardUserDefaults] valueForKey:[self.setting getKey]];
 
if (value == nil) {
value = [self.setting valueForKey:InAppSettingsSpecifierDefaultValue];
}
return value;
}
 
- (void) setValue:(id)newValue {
if ( self.setting.dataSource != nil )
[self.setting.dataSource setObject:newValue forKey:[self.setting getKey]];
else
[[NSUserDefaults standardUserDefaults] setObject:newValue forKey:[self.setting getKey]];
}
 
#pragma mark Table view methods
 
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
 
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.setting valueForKey:InAppSettingsSpecifierValues] count];
}
 
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * CellIdentifier = @"PSMultiValueSpecifierTableCell";
 
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
if (cell == nil) {
#if InAppSettingsUseNewCells
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
#else
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
#endif
}
 
NSString * cellTitle = InAppSettingsLocalize([[self.setting valueForKey:InAppSettingsSpecifierTitles] objectAtIndex:indexPath.row], self.setting.stringsTable);
id cellValue = [[self.setting valueForKey:InAppSettingsSpecifierValues] objectAtIndex:indexPath.row];
#if InAppSettingsUseNewCells
cell.textLabel.text = cellTitle;
#else
cell.text = cellTitle;
#endif
if ([cellValue isEqual:[self getValue]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
#if InAppSettingsUseNewCells
cell.textLabel.textColor = InAppSettingsBlue;
#else
cell.textColor = InAppSettingsBlue;
#endif
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
#if InAppSettingsUseNewCells
cell.textLabel.textColor = [UIColor blackColor];
#else
cell.textColor = [UIColor blackColor];
#endif
}
 
return cell;
}
 
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
}
 
- (NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
id cellValue = [[self.setting valueForKey:InAppSettingsSpecifierValues] objectAtIndex:indexPath.row];
 
[self setValue:cellValue];
[self.tableView reloadData];
return indexPath;
}
 
@end
 
/iKopter/trunk/Classes/InAppSettings/InAppSettingsPotiValueController.h
0,0 → 1,37
//
// InAppSettingsPotiValueController.h
// MK4PhoneNav
//
// Created by Frank Blumenberg on 05.05.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
 
#import <UIKit/UIKit.h>
#import "InAppSettings.h"
 
 
@interface InAppSettingsPotiValueController : UIViewController<InAppSettingsChildPane, UITextFieldDelegate> {
 
InAppSettingsSpecifier* setting;
 
UITextField *numberField;
UILabel *sliderLabel;
UISwitch *potiSwitch;
 
UIPickerView *potiPicker;
NSArray *pickerData;
}
 
@property (nonatomic, retain) NSArray *pickerData;
@property (nonatomic, retain) InAppSettingsSpecifier* setting;
 
@property (nonatomic, retain) IBOutlet UIPickerView *potiPicker;
@property (nonatomic, retain) IBOutlet UITextField *numberField;
@property (nonatomic, retain) IBOutlet UILabel *sliderLabel;
@property (nonatomic, retain) IBOutlet UISwitch *potiSwitch;
 
- (IBAction)textFieldDoneEditing:(id)sender;
- (IBAction)backgroundTap:(id)sender;
- (IBAction)toggleControls:(id)sender;
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsPotiValueController.m
0,0 → 1,166
//
// InAppSettingsPotiValueController.m
// MK4PhoneNav
//
// Created by Frank Blumenberg on 05.05.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
 
#import "InAppSettingsPotiValueController.h"
 
 
@implementation InAppSettingsPotiValueController
 
@synthesize setting;
@synthesize numberField;
@synthesize sliderLabel;
@synthesize potiSwitch;
@synthesize pickerData;
@synthesize potiPicker;
 
- (id) initWithSetting:(InAppSettingsSpecifier *)inputSetting {
if ((self = [super initWithNibName:@"InAppSettingsPotiValueController" bundle:nil])) {
self.setting=inputSetting;
NSArray *array = [[NSArray alloc] initWithObjects:
@"Poti1",
@"Poti2",
@"Poti3",
@"Poti4",
@"Poti5",
@"Poti6",
@"Poti7",
@"Poti8",
nil];
self.pickerData = array;
[array release];
}
return self;
}
 
 
- (void) viewDidLoad {
[super viewDidLoad];
self.title = [self.setting localizedTitle];
}
 
- (void)viewWillAppear:(BOOL)animated {
 
int value = [[setting getValue] intValue];
if (value<248) {
potiSwitch.on = NO;
numberField.alpha = 1.0f;
potiPicker.alpha = 0.0f;
numberField.text =[NSString stringWithFormat:@"%d",value];
}
else {
potiSwitch.on = YES;
numberField.alpha = 0.0f;
potiPicker.alpha = 1.0f;
numberField.text =@"0";
[potiPicker selectRow:255-value inComponent:0 animated:NO];
}
}
 
-(void) viewWillDisappear:(BOOL)animated
{
int value=0;
if(potiSwitch.on)
{
int row = [potiPicker selectedRowInComponent:0];
value = 255-row;
}
else
{
value = [numberField.text intValue];
}
 
[setting setValue:[NSNumber numberWithInt:value]];
}
 
- (void) dealloc {
[setting release];
[numberField release];
[sliderLabel release];
[potiSwitch release];
[pickerData release];
[potiPicker release];
[super dealloc];
}
 
- (id) getValue {
return nil;
}
 
- (void) setValue:(id)newValue {
}
 
- (IBAction)textFieldDoneEditing:(id)sender {
[sender resignFirstResponder];
}
 
- (IBAction)backgroundTap:(id)sender {
[numberField resignFirstResponder];
}
 
- (IBAction)toggleControls:(id)sender {
 
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
numberField.alpha = potiSwitch.on?0.0f:1.0f;
potiPicker.alpha = potiSwitch.on?1.0f:0.0f;
[UIView commitAnimations];
}
 
 
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
 
- (void)viewDidUnload {
self.numberField = nil;
self.sliderLabel = nil;
self.potiSwitch = nil;
self.pickerData = nil;
self.potiPicker = nil;
[super viewDidUnload];
}
 
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSMutableString* s = [textField.text mutableCopy];
[s replaceCharactersInRange:range withString:string];
 
int value = [s intValue];
[s release];
if (value <=245 ) {
return YES;
}
return NO;
}
 
#pragma mark -
#pragma mark Picker Data Source Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [pickerData count];
}
#pragma mark Picker Delegate Methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [pickerData objectAtIndex:row];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsPotiValueController.xib
0,0 → 1,648
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1024</int>
<string key="IBDocument.SystemVersion">10D573</string>
<string key="IBDocument.InterfaceBuilderVersion">783</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">460.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">107</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="1"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIPickerView" id="175389793">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">290</int>
<string key="NSFrame">{{0, 88}, {320, 216}}</string>
<reference key="NSSuperview" ref="191373211"/>
<float key="IBUIAlpha">0.0</float>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIShowsSelectionIndicator">YES</bool>
</object>
<object class="IBUITextField" id="164640674">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 88}, {280, 31}}</string>
<reference key="NSSuperview" ref="191373211"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIContentVerticalAlignment">0</int>
<string key="IBUIText"/>
<int key="IBUIBorderStyle">3</int>
<object class="NSColor" key="IBUITextColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
<object class="NSColorSpace" key="NSCustomColorSpace">
<int key="NSID">2</int>
</object>
</object>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica</string>
<double key="NSSize">17</double>
<int key="NSfFlags">16</int>
</object>
<bool key="IBUIAdjustsFontSizeToFit">YES</bool>
<float key="IBUIMinimumFontSize">17</float>
<object class="IBUITextInputTraits" key="IBUITextInputTraits">
<int key="IBUIAutocorrectionType">1</int>
<int key="IBUIKeyboardType">4</int>
<int key="IBUIReturnKeyType">9</int>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</object>
<object class="IBUISwitch" id="1007142614">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{206, 20}, {94, 27}}</string>
<reference key="NSSuperview" ref="191373211"/>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
</object>
<object class="IBUILabel" id="805205008">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 22}, {73, 22}}</string>
<reference key="NSSuperview" ref="191373211"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Use Poti</string>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica-Bold</string>
<double key="NSSize">17</double>
<int key="NSfFlags">16</int>
</object>
<object class="NSColor" key="IBUITextColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes>
</object>
<nil key="IBUIHighlightedColor"/>
<int key="IBUIBaselineAdjustment">1</int>
<float key="IBUIMinimumFontSize">10</float>
</object>
</object>
<string key="NSFrameSize">{320, 372}</string>
<reference key="NSSuperview"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC43MDE5NjA4MDIxIDAuNzAxOTYwODAyMSAwLjcwMTk2MDgwMjEAA</bytes>
</object>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<object class="IBUISimulatedNavigationBarMetrics" key="IBUISimulatedTopBarMetrics">
<bool key="IBUIPrompted">NO</bool>
</object>
<object class="IBUISimulatedToolbarMetrics" key="IBUISimulatedBottomBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="191373211"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">sliderLabel</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="805205008"/>
</object>
<int key="connectionID">8</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">numberField</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="164640674"/>
</object>
<int key="connectionID">9</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">potiSwitch</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="1007142614"/>
</object>
<int key="connectionID">10</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">potiPicker</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="175389793"/>
</object>
<int key="connectionID">11</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="175389793"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">12</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="175389793"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">13</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">backgroundTap:</string>
<reference key="source" ref="191373211"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">14</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">textFieldDoneEditing:</string>
<reference key="source" ref="164640674"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">19</int>
</object>
<int key="connectionID">15</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">toggleControls:</string>
<reference key="source" ref="1007142614"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">13</int>
</object>
<int key="connectionID">16</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="164640674"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">17</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<reference key="object" ref="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="1007142614"/>
<reference ref="805205008"/>
<reference ref="164640674"/>
<reference ref="175389793"/>
</object>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="175389793"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">5</int>
<reference key="object" ref="164640674"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">6</int>
<reference key="object" ref="1007142614"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">7</int>
<reference key="object" ref="805205008"/>
<reference key="parent" ref="191373211"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.CustomClassName</string>
<string>-2.CustomClassName</string>
<string>1.CustomClassName</string>
<string>1.IBEditorWindowLastContentRect</string>
<string>1.IBPluginDependency</string>
<string>4.IBPluginDependency</string>
<string>5.IBPluginDependency</string>
<string>6.IBPluginDependency</string>
<string>7.IBPluginDependency</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>InAppSettingsPotiValueController</string>
<string>UIResponder</string>
<string>UIControl</string>
<string>{{673, 278}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">18</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">InAppSettingsPotiValueController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="actions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>backgroundTap:</string>
<string>textFieldDoneEditing:</string>
<string>toggleControls:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>backgroundTap:</string>
<string>textFieldDoneEditing:</string>
<string>toggleControls:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">backgroundTap:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">textFieldDoneEditing:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">toggleControls:</string>
<string key="candidateClassName">id</string>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>numberField</string>
<string>potiPicker</string>
<string>potiSwitch</string>
<string>sliderLabel</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UITextField</string>
<string>UIPickerView</string>
<string>UISwitch</string>
<string>UILabel</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>numberField</string>
<string>potiPicker</string>
<string>potiSwitch</string>
<string>sliderLabel</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">numberField</string>
<string key="candidateClassName">UITextField</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">potiPicker</string>
<string key="candidateClassName">UIPickerView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">potiSwitch</string>
<string key="candidateClassName">UISwitch</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">sliderLabel</string>
<string key="candidateClassName">UILabel</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">InAppSettingsPotiValueController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../MKCommunication/AsyncSocket.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="938759004">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIControl</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIControl.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UILabel</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UILabel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIPickerView</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPickerView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIResponder</string>
<string key="superclassName">NSObject</string>
<reference key="sourceIdentifier" ref="938759004"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchBar</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchDisplayController</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISwitch</string>
<string key="superclassName">UIControl</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISwitch.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UITextField</string>
<string key="superclassName">UIControl</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="360062614">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<reference key="sourceIdentifier" ref="360062614"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">MK4PhoneNav.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">107</string>
</data>
</archive>
/iKopter/trunk/Classes/InAppSettings/InAppSettingsReader.h
0,0 → 1,36
//
// InAppSettingsReader.h
// InAppSettingsTestApp
//
// Created by David Keegan on 1/19/10.
// Copyright 2010 InScopeApps{+}. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
@protocol InAppSettingsDatasource;
 
@interface InAppSettingsReaderRegisterDefaults : NSObject {
// keep track of what files we've read to avoid circular references
NSMutableArray * files;
NSMutableDictionary * values;
}
 
@property (nonatomic, retain) NSMutableArray * files;
@property (nonatomic, retain) NSMutableDictionary * values;
 
@end
 
@interface InAppSettingsReader : NSObject {
NSString * file;
NSMutableArray * headers, * settings;
id<InAppSettingsDatasource> dataSource;
}
 
@property (nonatomic, copy) NSString * file;
@property (nonatomic, retain) NSMutableArray * headers, * settings;
 
- (id) initWithFile:(NSString *)inputFile;
- (id) initWithFile:(NSString *)inputFile dataSource:(id<InAppSettingsDatasource>)aDataSource;
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsReader.m
0,0 → 1,128
//
// InAppSettingsReader.m
// InAppSettingsTestApp
//
// Created by David Keegan on 1/19/10.
// Copyright 2010 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettingsReader.h"
#import "InAppSettingsSpecifier.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsReaderRegisterDefaults
 
@synthesize files;
@synthesize values;
 
- (void) loadFile:(NSString *)file {
// if the file is not in the files list we havn't read it yet
NSInteger fileIndex = [self.files indexOfObject:file];
 
if (fileIndex == NSNotFound) {
[self.files addObject:file];
 
// load plist
NSDictionary * settingsDictionary = [[NSDictionary alloc] initWithContentsOfFile:InAppSettingsFullPlistPath(file)];
NSArray * preferenceSpecifiers = [settingsDictionary objectForKey:InAppSettingsPreferenceSpecifiers];
NSString * stringsTable = [settingsDictionary objectForKey:InAppSettingsStringsTable];
 
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
for (NSDictionary * eachSetting in preferenceSpecifiers) {
InAppSettingsSpecifier * setting = [[InAppSettingsSpecifier alloc] initWithDictionary:eachSetting andStringsTable:stringsTable];
if ([setting isValid]) {
if ([setting isType:InAppSettingsPSChildPaneSpecifier]) {
[self loadFile:[setting valueForKey:InAppSettingsSpecifierFile]];
} else if ([setting hasKey]) {
if ([setting valueForKey:InAppSettingsSpecifierDefaultValue]) {
[self.values
setObject:[setting valueForKey:InAppSettingsSpecifierDefaultValue]
forKey:[setting getKey]];
}
}
}
[setting release];
}
[pool drain];
[settingsDictionary release];
}
}
 
- (id) init {
self = [super init];
if (self != nil) {
self.files = [[NSMutableArray alloc] init];
self.values = [[NSMutableDictionary alloc] init];
[self loadFile:InAppSettingsRootFile];
[[NSUserDefaults standardUserDefaults] registerDefaults:self.values];
}
return self;
}
 
 
- (void) dealloc {
[files release];
[values release];
[super dealloc];
}
 
@end
 
@implementation InAppSettingsReader
 
@synthesize file;
@synthesize headers, settings;
 
- (id) initWithFile:(NSString *)inputFile {
self = [super init];
if (self != nil) {
self.file = inputFile;
 
// load plist
NSDictionary * settingsDictionary = [[NSDictionary alloc] initWithContentsOfFile:InAppSettingsFullPlistPath(self.file)];
NSArray * preferenceSpecifiers = [settingsDictionary objectForKey:InAppSettingsPreferenceSpecifiers];
NSString * stringsTable = [settingsDictionary objectForKey:InAppSettingsStringsTable];
 
// initialize the arrays
headers = [[NSMutableArray alloc] init];
settings = [[NSMutableArray alloc] init];
 
// load the data
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
for (NSDictionary * eachSetting in preferenceSpecifiers) {
InAppSettingsSpecifier * setting = [[InAppSettingsSpecifier alloc] initWithDictionary:eachSetting andStringsTable:stringsTable];
setting.dataSource = dataSource;
if ([setting isValid]) {
if ([setting isType:InAppSettingsPSGroupSpecifier]) {
[self.headers addObject:[setting localizedTitle]];
[self.settings addObject:[NSMutableArray array]];
} else {
// if there are no settings make an initial container
if ([self.settings count] < 1) {
[self.headers addObject:@""];
[self.settings addObject:[NSMutableArray array]];
}
[[self.settings lastObject] addObject:setting];
}
}
[setting release];
}
[pool drain];
[settingsDictionary release];
}
return self;
}
 
- (id) initWithFile:(NSString *)inputFile dataSource:(id<InAppSettingsDatasource>)aDataSource {
dataSource = aDataSource;
return [self initWithFile:inputFile];
}
 
- (void) dealloc {
[file release];
[headers release];
[settings release];
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsSpecifier.h
0,0 → 1,47
//
// InAppSetting.h
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
@protocol InAppSettingsSpecifierDelegate;
@protocol InAppSettingsDatasource;
 
@interface InAppSettingsSpecifier : NSObject {
NSString * stringsTable;
NSDictionary * settingDictionary;
id<InAppSettingsSpecifierDelegate> delegate;
id<InAppSettingsDatasource> dataSource;
}
 
@property (nonatomic, copy) NSString * stringsTable;
@property (assign) id<InAppSettingsSpecifierDelegate> delegate;
@property (assign) id<InAppSettingsDatasource> dataSource;
 
- (NSString *) getKey;
- (NSString *) getType;
- (BOOL) isType:(NSString *)type;
- (id) getValue;
- (void) setValue:(id)newValue;
- (id) valueForKey:(NSString *)key;
- (NSString *) localizedTitle;
- (NSString *) cellName;
 
- (BOOL) hasTitle;
- (BOOL) hasKey;
- (BOOL) hasDefaultValue;
- (BOOL) isValid;
 
- (id) initWithDictionary:(NSDictionary *)dictionary andStringsTable:(NSString *)table;
 
@end
 
@protocol InAppSettingsSpecifierDelegate < NSObject >
 
- (void) settingsSpecifierUpdated:(InAppSettingsSpecifier *)specifier;
 
@end
/iKopter/trunk/Classes/InAppSettings/InAppSettingsSpecifier.m
0,0 → 1,235
//
// InAppSetting.m
// InAppSettingsTestApp
//
// Created by David Keegan on 11/21/09.
// Copyright 2009 InScopeApps{+}. All rights reserved.
//
 
#import "InAppSettings.h"
#import "InAppSettingsSpecifier.h"
#import "InAppSettingsConstants.h"
 
@implementation InAppSettingsSpecifier
 
@synthesize stringsTable;
@synthesize delegate;
@synthesize dataSource;
 
- (NSString *) getKey {
return [self valueForKey:InAppSettingsSpecifierKey];
}
 
- (NSString *) getType {
return [self valueForKey:InAppSettingsSpecifierType];
}
 
- (BOOL) isType:(NSString *)type {
return [[self getType] isEqualToString:type];
}
 
- (id) valueForKey:(NSString *)key {
return [settingDictionary objectForKey:key];
}
 
- (NSString *) localizedTitle {
NSString * title = [self valueForKey:InAppSettingsSpecifierTitle];
 
if ([self valueForKey:InAppSettingsSpecifierInAppTitle]) {
title = [self valueForKey:InAppSettingsSpecifierInAppTitle];
}
return InAppSettingsLocalize(title, self.stringsTable);
}
 
- (NSString *) cellName {
 
if ([self valueForKey:InAppSettingsSpecifierInAppCellClass]) {
return [self valueForKey:InAppSettingsSpecifierInAppCellClass];
}
 
return [NSString stringWithFormat:@"%@%@Cell", InAppSettingsProjectName, [self getType]];
}
 
- (id) getValue {
 
id value;
 
if ( self.dataSource != nil )
value = [self.dataSource objectForKey:[self getKey]];
else
value = [[NSUserDefaults standardUserDefaults] valueForKey:[self getKey]];
 
if (value == nil) {
value = [self valueForKey:InAppSettingsSpecifierDefaultValue];
}
return value;
}
 
- (void) setValue:(id)newValue {
NSString * key = [self getKey];
 
if ( self.dataSource != nil ) {
[self.dataSource setObject:newValue forKey:key];
} else
[[NSUserDefaults standardUserDefaults] setObject:newValue forKey:key];
 
[self.delegate settingsSpecifierUpdated:self];
}
 
#pragma mark validation
 
- (BOOL) hasTitle {
return ([self valueForKey:InAppSettingsSpecifierTitle]) ? YES : NO;
}
 
- (BOOL) hasKey {
NSString * key = [self getKey];
 
return (key && (![key isEqualToString:@""]));
}
 
- (BOOL) hasDefaultValue {
return ([self valueForKey:InAppSettingsSpecifierDefaultValue]) ? YES : NO;
}
 
- (BOOL) isValid {
if (![self getType]) {
return NO;
}
 
if ([self isType:InAppSettingsPSGroupSpecifier]) {
return YES;
}
 
if ([self isType:InAppSettingsPSMultiValueSpecifier]) {
if (![self hasKey]) {
return NO;
}
 
if (![self hasDefaultValue]) {
return NO;
}
 
// check the localized and un-locatlized values
if (![self hasTitle] || [[self valueForKey:InAppSettingsSpecifierTitle] length] == 0) {
return NO;
}
 
NSArray * titles = [self valueForKey:InAppSettingsSpecifierTitles];
if ((!titles) || ([titles count] == 0)) {
return NO;
}
 
NSArray * values = [self valueForKey:InAppSettingsSpecifierValues];
if ((!values) || ([values count] == 0)) {
return NO;
}
 
if ([titles count] != [values count]) {
return NO;
}
 
return YES;
}
 
if ([self isType:InAppSettingsPSSliderSpecifier]) {
if (![self hasKey]) {
return NO;
}
 
if (![self hasDefaultValue]) {
return NO;
}
 
// The settings app allows min>max
if (![self valueForKey:InAppSettingsSpecifierMinimumValue]) {
return NO;
}
 
if (![self valueForKey:InAppSettingsSpecifierMaximumValue]) {
return NO;
}
 
return YES;
}
 
if ([self isType:InAppSettingsPSToggleSwitchSpecifier]) {
if (![self hasKey]) {
return NO;
}
 
if (![self hasDefaultValue]) {
return NO;
}
 
if (![self hasTitle]) {
return NO;
}
 
return YES;
}
 
if ([self isType:InAppSettingsPSTitleValueSpecifier]) {
if (![self hasKey]) {
return NO;
}
 
if (![self hasDefaultValue]) {
return NO;
}
 
return YES;
}
 
if ([self isType:InAppSettingsPSTextFieldSpecifier]) {
if (![self hasKey]) {
return NO;
}
 
if (![self hasTitle]) {
return NO;
}
 
return YES;
}
 
if ([self isType:InAppSettingsPSChildPaneSpecifier]) {
if (![self hasTitle]) {
return NO;
}
 
if (![self valueForKey:InAppSettingsSpecifierFile]) {
return NO;
}
 
return YES;
}
 
return NO;
}
 
#pragma mark init/dealloc
 
- (id) init {
return [self initWithDictionary:nil andStringsTable:nil];
}
 
- (id) initWithDictionary:(NSDictionary *)dictionary andStringsTable:(NSString *)table {
self = [super init];
if (self != nil) {
if (dictionary) {
self.stringsTable = table;
settingDictionary = [dictionary retain];
}
}
return self;
}
 
- (void) dealloc {
self.delegate = nil;
[stringsTable release];
[settingDictionary release];
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/Settings/SettingViewController.h
0,0 → 1,40
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
#import "InAppSettings.h"
 
@interface SettingViewController : InAppSettingsViewController<InAppSettingsDatasource> {
 
NSMutableDictionary* _setting;
}
 
@property(nonatomic,retain) NSMutableDictionary* setting;
 
- (id)initWithSettingDatasource:(NSMutableDictionary*)aSetting;
 
- (void)saveSetting:(id)sender;
- (void)reloadSetting:(id)sender;
@end
/iKopter/trunk/Classes/Settings/SettingViewController.m
0,0 → 1,183
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "SettingViewController.h"
#import "InAppSettings.h"
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "NSData+MKPayloadEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
@implementation SettingViewController
 
@synthesize setting=_setting;
 
#pragma mark -
 
- (id)initWithSettingDatasource:(NSMutableDictionary*)aSetting {
 
if ((self = [super initWithFile:@"Setting"])) {
self.setting = aSetting;
super.dataSource = self;
}
return self;
}
 
- (void)dealloc {
self.setting=nil;
[_setting release];
[super dealloc];
}
 
#pragma mark -
#pragma mark View lifecycle
 
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem* renameButton;
renameButton = [[[UIBarButtonItem alloc]
initWithTitle:NSLocalizedString(@"Save",@"Save toolbar item")
style:UIBarButtonItemStyleDone
target:self
action:@selector(saveSetting:)] autorelease];
 
UIBarButtonItem* spacer;
spacer = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil] autorelease];
UIBarButtonItem* reloadButton;
reloadButton = [[[UIBarButtonItem alloc]
initWithTitle:NSLocalizedString(@"Reload", @"Reload toolbar item")
style:UIBarButtonItemStyleBordered
target:self
action:@selector(reloadSetting:)] autorelease];
[self setToolbarItems:[NSArray arrayWithObjects:renameButton,spacer,reloadButton,nil]];
}
 
- (void)viewDidUnload {
[super viewDidUnload];
 
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
DLog(@"unload");
}
 
#pragma mark -
 
// called after this controller's view will appear
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setToolbarHidden:NO animated:NO];
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(readSettingNotification:)
name:MKReadSettingNotification
object:nil];
[nc addObserver:self
selector:@selector(writeSettingNotification:)
name:MKWriteSettingNotification
object:nil];
}
 
// called after this controller's view will appear
- (void)viewWillDisappear:(BOOL)animated {
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
}
 
 
#pragma mark -
#pragma mark Save Setting
 
- (void)saveSetting:(id)sender {
DLog(@"save setting");
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
 
NSData * payload = [NSData payloadForWriteSettingRequest:self.setting];
NSData * data = [payload dataWithCommand:MKCommandWriteSettingsRequest
forAddress:MKAddressFC];
[cCtrl sendRequest:data];
}
 
- (void) writeSettingNotification:(NSNotification *)aNotification {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Setting" message:@"Setting saved"
delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
 
#pragma mark -
#pragma mark Reload Setting
 
- (void)reloadSetting:(id)sender {
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
NSNumber* theIndex = [self.setting objectForKey:kMKDataKeyIndex];
uint8_t index = [theIndex unsignedCharValue];
NSData * data = [NSData dataWithCommand:MKCommandReadSettingsRequest
forAddress:MKAddressFC
payloadWithBytes:&index
length:1];
[cCtrl sendRequest:data];
}
 
- (void) readSettingNotification:(NSNotification *)aNotification {
NSDictionary* d=[aNotification userInfo];
self.setting = [d mutableCopy];
[super.settingsTableView reloadData];
}
 
#pragma mark -
#pragma mark InAppSettingsDatasource
 
- (id) objectForKey:(id)aKey {
return [self.setting objectForKey:aKey];
}
 
- (void) setObject:(id)anObject forKey:(id)aKey {
[self.setting setObject:anObject forKey:aKey];
}
 
 
@end
/iKopter/trunk/Classes/Settings/SettingsSelectionViewController.h
0,0 → 1,46
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
 
 
@interface SettingsSelectionViewController : UITableViewController {
 
NSMutableArray* _settings;
int activeSetting;
int newActiveSetting;
}
 
@property (nonatomic, retain) NSMutableArray* settings;
 
- (void) requestSettingForIndex:(NSInteger)theIndex;
 
- (IBAction) reloadAllSettings;
 
 
- (void)saveActiveSetting:(id)sender;
- (void)editActiveSetting:(id)sender;
- (void)cancelEditActiveSetting:(id)sender;
 
@end
/iKopter/trunk/Classes/Settings/SettingsSelectionViewController.m
0,0 → 1,454
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "SettingsSelectionViewController.h"
#import "SettingViewController.h"
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "NSData+MKPayloadEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
#import "InAppSettings.h"
#import "MixerViewController.h"
#import "ChannelsViewController.h"
#import "EngineTestViewController.h"
 
static NSUInteger kNumberOfPages = 5;
 
@interface SettingsSelectionViewController (Private)
 
@end
 
// ///////////////////////////////////////////////////////////////////////////////
 
@implementation SettingsSelectionViewController
 
@synthesize settings=_settings;
 
// ///////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark View lifecycle
 
 
- (id)init {
if ((self = [super initWithStyle:UITableViewStyleGrouped])) {
self.title = NSLocalizedString(@"Settings",@"Settings controller title");
self.hidesBottomBarWhenPushed=NO;
}
return self;
}
 
- (void)viewDidLoad {
[super viewDidLoad];
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(readSettingNotification:)
name:MKReadSettingNotification
object:nil];
 
[nc addObserver:self
selector:@selector(changeSettingNotification:)
name:MKChangeSettingNotification
object:nil];
[self.tableView setAllowsSelectionDuringEditing:YES];
}
 
#pragma mark -
#pragma mark Memory management
 
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
 
- (void)viewDidUnload {
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
self.settings=nil;
[_settings release];
}
 
 
- (void)dealloc {
[super dealloc];
[_settings release];
}
 
// ////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
- (void) viewWillAppear:(BOOL)animated {
[self.navigationController setToolbarHidden:NO animated:NO];
[[MKConnectionController sharedMKConnectionController] activateFlightCtrl];
}
 
- (void) viewDidAppear:(BOOL)animated {
[self cancelEditActiveSetting:self];
[self reloadAllSettings];
UIBarButtonItem* actionButton;
actionButton = [[[UIBarButtonItem alloc]
initWithTitle:@"Change Active"
style:UIBarButtonItemStyleBordered|UIBarButtonItemStyleDone
target:self
action:@selector(saveActiveSetting:)] autorelease];
[self setToolbarItems:[NSArray arrayWithObject:actionButton] animated:YES];
//[actionButton release];
}
 
// ////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
- (IBAction) reloadAllSettings {
 
NSMutableArray * settings = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[settings addObject:[NSNull null]];
}
self.settings = settings;
[settings release];
activeSetting=0xFF;
[self requestSettingForIndex:0xFF];
}
 
 
// theIndex 1-5 or 0xFF
- (void) requestSettingForIndex:(NSInteger)theIndex {
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
uint8_t index = theIndex;
NSData * data = [NSData dataWithCommand:MKCommandReadSettingsRequest
forAddress:MKAddressFC
payloadWithBytes:&index
length:1];
[cCtrl sendRequest:data];
}
 
 
- (void) readSettingNotification:(NSNotification *)aNotification {
 
NSDictionary* d=[aNotification userInfo];
 
NSInteger index = [[d objectForKey:kMKDataKeyIndex] integerValue]-1;
if (activeSetting==0xFF) {
activeSetting=index;
}
[self.settings replaceObjectAtIndex:index withObject:d];
for (int i=0; i<kNumberOfPages; i++) {
if ([self.settings objectAtIndex:i] == [NSNull null]) {
[self requestSettingForIndex:i+1];
break;
}
}
[self.tableView reloadData];
}
 
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Table view data source
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.tableView.editing?1:2;
}
 
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section==0)
return [self.settings count];
return 3;
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
 
- (UITableViewCell *) cellForSetting: (UITableView *) tableView indexPath: (NSIndexPath *) indexPath {
static NSString *CellIdentifier = @"SettingsSelectionCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
 
NSUInteger row = [indexPath row];
NSDictionary* setting=[self.settings objectAtIndex:row];
 
if ((NSNull *)setting == [NSNull null]) {
cell.textLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Setting #%d",@"Setting i"), row];
UIActivityIndicatorView *activityView =
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activityView startAnimating];
[cell setAccessoryView:activityView];
[activityView release];
}
else {
 
cell.textLabel.text = [setting objectForKey:kKeyName];
cell.accessoryView = nil;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
 
int currActiveSetting= self.tableView.editing? newActiveSetting:activeSetting;
if( row == currActiveSetting ){
UIImage *image = [UIImage imageNamed:@"star.png"];
cell.imageView.image = image;
}
else {
cell.imageView.image = nil;
}
return cell;
 
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
 
- (UITableViewCell *) cellForExtra: (UITableView *) tableView indexPath: (NSIndexPath *) indexPath {
static NSString *CellIdentifier = @"SettingsMixerCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
switch (indexPath.row) {
case 0:
cell.textLabel.text = NSLocalizedString(@"Mixer",@"Mixer cell");
break;
case 1:
cell.textLabel.text = NSLocalizedString(@"Motor test",@"Motor test cell");
break;
case 2:
cell.textLabel.text = NSLocalizedString(@"Channels",@"Channels cell");
break;
}
cell.accessoryView = nil;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.imageView.image = nil;
return cell;
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section==0)
return [self cellForSetting: tableView indexPath: indexPath];
 
return [self cellForExtra: tableView indexPath: indexPath];
}
 
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
 
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
 
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return indexPath.section==0;
}
 
 
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
 
 
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
 
 
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
 
- (void) changeSettingNotification:(NSNotification *)aNotification {
NSDictionary* d=[aNotification userInfo];
NSInteger index = [[d objectForKey:kMKDataKeyIndex] integerValue]-1;
activeSetting=index;
[self cancelEditActiveSetting:self];
}
 
- (void)saveActiveSetting:(id)sender
{
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
uint8_t index = newActiveSetting+1;
NSData * data = [NSData dataWithCommand:MKCommandChangeSettingsRequest
forAddress:MKAddressFC
payloadWithBytes:&index
length:1];
[cCtrl sendRequest:data];
}
 
- (void)editActiveSetting:(id)sender
{
[self.navigationController setToolbarHidden:NO animated:YES];
UIBarButtonItem *cancelButton = [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:@selector(cancelEditActiveSetting:)]autorelease];
[self.navigationItem setRightBarButtonItem:cancelButton animated:NO];
newActiveSetting = activeSetting;
[self.tableView setEditing:YES animated:YES];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
}
 
- (void)cancelEditActiveSetting:(id)sender
{
[self.navigationController setToolbarHidden:YES animated:YES];
UIBarButtonItem *editButton = [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemEdit
target:self
action:@selector(editActiveSetting:)]autorelease];
[self.navigationItem setRightBarButtonItem:editButton animated:NO];
[self.tableView setEditing:NO animated:YES];
//[self.tableView insertSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:YES];
[self.tableView reloadData];
}
 
 
#pragma mark -
#pragma mark Table view delegate
 
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if( self.tableView.editing && indexPath.section!=0 )
return nil;
return indexPath;
}
 
 
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSUInteger row = [indexPath row];
if (self.tableView.editing ) {
if(indexPath.section==0 && newActiveSetting != row) {
NSArray *row0 = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:newActiveSetting inSection:0]];
newActiveSetting = row;
NSArray *row1 = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:newActiveSetting inSection:0]];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:row0 withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadRowsAtIndexPaths:row1 withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
}
else {
if( indexPath.section==0 ) {
NSMutableDictionary* setting=[self.settings objectAtIndex:row];
SettingViewController* settingView = [[SettingViewController alloc] initWithSettingDatasource:setting];
[self.navigationController setToolbarHidden:NO animated:NO];
[self.navigationController pushViewController:settingView animated:YES];
[settingView release];
}
else {
MixerViewController* extraView=nil;
 
switch (indexPath.row) {
case 0:
extraView = [[MixerViewController alloc] initWithStyle:UITableViewStylePlain];
break;
case 1:
extraView = [[EngineTestViewController alloc] initWithStyle:UITableViewStyleGrouped];
break;
case 2:
extraView = [[ChannelsViewController alloc] initWithStyle:UITableViewStylePlain];
break;
}
[self.navigationController setToolbarHidden:NO animated:NO];
[self.navigationController pushViewController:extraView animated:YES];
[extraView release];
}
}
}
 
 
 
 
@end
 
/iKopter/trunk/Classes/Views/AnalogValues.h
0,0 → 1,51
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
#import <Foundation/Foundation.h>
 
@protocol AnalogValuesDelegate
 
- (void) didReceiveValues;
- (void) didReceiveLabelForIndexPath:(NSIndexPath *)indexPath;
- (void) changed;
 
@end
 
@interface AnalogValues : NSObject {
 
NSMutableArray * analogLabels;
NSMutableArray * debugData;
int debugResponseCounter;
id<AnalogValuesDelegate> _delegate;
}
 
@property(assign) id<AnalogValuesDelegate> delegate;
 
-(NSUInteger) count;
-(NSString*) labelAtIndexPath:(NSIndexPath *)indexPath;
-(NSString*) valueAtIndexPath:(NSIndexPath *)indexPath;
-(void) reloadAll;
 
@end
/iKopter/trunk/Classes/Views/AnalogValues.m
0,0 → 1,221
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "AnalogValues.h"
#import "TTGlobalCorePaths.h"
#import "TTCorePreprocessorMacros.h"
 
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
 
#define kAnalogLabelFile @"analoglables.plist"
 
@interface AnalogValues (Private)
- (void) analogLabelNotification:(NSNotification *)aNotification;
- (void) debugValueNotification:(NSNotification *)aNotification;
- (void) requestAnalogLabelForIndex:(NSInteger)index;
- (void) requestDebugData;
 
- (void) loadLabels;
- (void) saveLabels;
- (void) initNotifications;
 
@end
 
@implementation AnalogValues
 
@synthesize delegate = _delegate;
 
- (id) init
{
self = [super init];
if (self != nil) {
debugData = [[NSMutableArray alloc] initWithCapacity:kMaxDebugData];
for (int i = 0; i < kMaxDebugData; i++) {
[debugData addObject:[NSNumber numberWithInt:0]];
}
[self loadLabels];
[self initNotifications];
}
return self;
}
 
- (void) dealloc {
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
[analogLabels release];
[debugData release];
[super dealloc];
}
 
-(void) loadLabels {
NSString *filePath = TTPathForDocumentsResource(kAnalogLabelFile);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
DLog(@"Load the analog labels from %@",filePath);
analogLabels = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
}
else {
analogLabels = [[NSMutableArray alloc] initWithCapacity:4];
for(int d=0;d<4;d++) {
NSMutableArray* analogLabelsDevice = [NSMutableArray arrayWithCapacity:kMaxDebugData];
for (int i = 0; i < kMaxDebugData; i++) {
[analogLabelsDevice addObject:[NSString stringWithFormat:@"Analog%d", i]];
}
[analogLabels addObject:analogLabelsDevice];
}
[self saveLabels];
}
}
 
-(void) saveLabels {
NSString *filePath = TTPathForDocumentsResource(kAnalogLabelFile);
[analogLabels writeToFile:filePath atomically:NO];
}
-(void) initNotifications {
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
 
[nc addObserver:self
selector:@selector(analogLabelNotification:)
name:MKDebugLabelNotification
object:nil];
[nc addObserver:self
selector:@selector(debugValueNotification:)
name:MKDebugDataNotification
object:nil];
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
- (void) requestAnalogLabelForIndex:(NSInteger)theIndex {
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
uint8_t index = theIndex;
NSData * data = [NSData dataWithCommand:MKCommandDebugLabelRequest
forAddress:MKAddressAll
payloadWithBytes:&index
length:1];
[cCtrl sendRequest:data];
}
 
- (void) analogLabelNotification:(NSNotification *)aNotification {
 
NSString * label = [[aNotification userInfo] objectForKey:kMKDataKeyLabel];
NSInteger index = [[[aNotification userInfo] objectForKey:kMKDataKeyIndex] intValue];
NSUInteger address=[MKConnectionController sharedMKConnectionController].currentDevice;
if ([label length] > 0) {
// label=[NSString stringWithFormat:@"%@ %d",label,[label length]];
NSMutableArray* a=[analogLabels objectAtIndex:address];
[a replaceObjectAtIndex:index withObject:label];
}
DLog(@"([%d][%d] %@",address , index, label);
if (index < (kMaxDebugData-1)) {
[self requestAnalogLabelForIndex:index+1];
}
else {
[self saveLabels];
}
 
[self.delegate didReceiveLabelForIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
- (void) requestDebugData {
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
uint8_t interval = 50;
NSData * data = [NSData dataWithCommand:MKCommandDebugValueRequest
forAddress:MKAddressAll
payloadWithBytes:&interval
length:1];
[cCtrl sendRequest:data];
}
 
- (void) debugValueNotification:(NSNotification *)aNotification {
NSArray * newDebugData = [[aNotification userInfo] objectForKey:kMKDataKeyDebugData];
for (int i = 0; i < [newDebugData count]; i++) {
[debugData replaceObjectAtIndex:i withObject:[newDebugData objectAtIndex:i]];
}
if (debugResponseCounter++ > 4 ) {
[self requestDebugData];
debugResponseCounter = 0;
}
 
[self.delegate didReceiveValues];
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
-(void) reloadAll {
[self performSelector:@selector(requestDebugData) withObject:self afterDelay:0.1];
 
[self requestAnalogLabelForIndex:0];
}
 
-(NSUInteger) count {
return [debugData count];
}
 
-(NSString*) labelAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger address=[MKConnectionController sharedMKConnectionController].currentDevice;
NSMutableArray* a=[analogLabels objectAtIndex:address];
 
return [a objectAtIndex:indexPath.row];
}
 
-(NSString*) valueAtIndexPath:(NSIndexPath *)indexPath {
return [[debugData objectAtIndex:indexPath.row] description];
}
 
@end
/iKopter/trunk/Classes/Views/AnalogViewController.h
0,0 → 1,35
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
#import "AnalogValues.h"
 
@interface AnalogViewController : UITableViewController<AnalogValuesDelegate> {
AnalogValues* values;
UISegmentedControl* segment;
}
- (IBAction) changeDevice;
 
@end
/iKopter/trunk/Classes/Views/AnalogViewController.m
0,0 → 1,191
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "AnalogViewController.h"
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
#import "TTCorePreprocessorMacros.h"
 
// ///////////////////////////////////////////////////////////////////////////////
 
@implementation AnalogViewController
 
- (void) updateSegment {
if ([[MKConnectionController sharedMKConnectionController] hasNaviCtrl]) {
[segment setEnabled:YES forSegmentAtIndex:0];
[segment setEnabled:YES forSegmentAtIndex:1];
[segment setEnabled:YES forSegmentAtIndex:2];
}
else {
[segment setEnabled:NO forSegmentAtIndex:0];
[segment setEnabled:YES forSegmentAtIndex:1];
[segment setEnabled:NO forSegmentAtIndex:2];
}
MKAddress currentDevice=[MKConnectionController sharedMKConnectionController].currentDevice;
switch (currentDevice) {
case MKAddressNC:
segment.selectedSegmentIndex=0;
break;
case MKAddressFC:
segment.selectedSegmentIndex=1;
break;
case MKAddressMK3MAg:
segment.selectedSegmentIndex=2;
break;
default:
break;
}
}
 
#pragma mark -
#pragma mark View lifecycle
 
- (void) viewDidLoad {
 
UIBarButtonItem* spacer;
spacer = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil] autorelease];
NSArray* segmentItems = [NSArray arrayWithObjects:@"NaviCtrl",@"FlightCtrl",@"MK3Mag",nil];
segment = [[UISegmentedControl alloc] initWithItems:segmentItems];
segment.segmentedControlStyle=UISegmentedControlStyleBar;
 
[segment addTarget:self
action:@selector(changeDevice)
forControlEvents:UIControlEventValueChanged];
UIBarButtonItem* segmentButton;
segmentButton = [[[UIBarButtonItem alloc]
initWithCustomView:segment] autorelease];
[self setToolbarItems:[NSArray arrayWithObjects:spacer,segmentButton,spacer,nil]];
 
}
 
- (void) viewWillAppear:(BOOL)animated {
values = [[AnalogValues alloc] init];
values.delegate = self;
[self updateSegment];
}
 
- (void) viewDidAppear:(BOOL)animated {
[values reloadAll];
}
 
- (void) viewDidDisappear:(BOOL)animated {
TT_RELEASE_SAFELY(values);
}
 
- (void) viewDidUnload {
TT_RELEASE_SAFELY(segment);
}
 
#pragma mark -
 
-(void) reloadAll {
[values reloadAll];
}
 
- (IBAction) changeDevice {
[[MKConnectionController sharedMKConnectionController] activateNaviCtrl];
switch (segment.selectedSegmentIndex) {
case 1:
[[MKConnectionController sharedMKConnectionController] activateFlightCtrl];
break;
case 2:
[[MKConnectionController sharedMKConnectionController] activateMK3MAG];
break;
}
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
DLog(@"Device set to %d", cCtrl.currentDevice);
[self performSelector:@selector(reloadAll) withObject:self afterDelay:0.1];
}
 
 
 
#pragma mark -
#pragma mark Table view data source
 
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
 
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [values count];
}
 
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * CellIdentifier = @"AnalogTableCell";
 
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
 
cell.textLabel.text = [values labelAtIndexPath:indexPath];
cell.detailTextLabel.text = [values valueAtIndexPath:indexPath];
 
return cell;
}
 
#pragma mark -
#pragma mark Table view delegate
 
- (NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
 
#pragma mark -
#pragma mark Analog values delegate
 
- (void) didReceiveValues {
[self.tableView reloadData];
}
 
- (void) didReceiveLabelForIndexPath:(NSIndexPath *)indexPath {
[self.tableView reloadData];
}
 
- (void) changed {
[self.tableView reloadData];
}
 
@end
 
/iKopter/trunk/Classes/Views/ChannelsViewCell.h
0,0 → 1,19
//
// ChannelsViewCell.h
// myKopter
//
// Created by Frank Blumenberg on 16.05.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
 
#import <UIKit/UIKit.h>
 
 
@interface ChannelsViewCell : UITableViewCell {
 
UIProgressView* progressBar;
}
 
-(void)setChannelValue:(int16_t) value;
 
@end
/iKopter/trunk/Classes/Views/ChannelsViewCell.m
0,0 → 1,43
//
// ChannelsViewCell.m
// myKopter
//
// Created by Frank Blumenberg on 16.05.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
 
#import "ChannelsViewCell.h"
 
#define kUIProgressBarWidth 160.0
#define kUIProgressBarHeight 24.0
 
@implementation ChannelsViewCell
 
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
 
CGRect frame = CGRectMake(126.0, 20.0, kUIProgressBarWidth, kUIProgressBarHeight);
progressBar = [[UIProgressView alloc] initWithFrame:frame];
progressBar.progressViewStyle = UIProgressViewStyleDefault;
progressBar.progress = 0.5; // Initialization code
[self.contentView addSubview:progressBar];
}
return self;
}
 
- (void)dealloc {
[progressBar release];
[super dealloc];
}
 
-(void)setChannelValue:(int16_t)value {
progressBar.progress = (value+125)/250.0f;
}
 
 
 
@end
/iKopter/trunk/Classes/Views/ChannelsViewController.h
0,0 → 1,36
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
 
@interface ChannelsViewController : UITableViewController {
 
int16_t channelValues[26];
NSTimer* _updateTimer;
}
 
@property(nonatomic,retain) NSTimer* updateTimer;
 
@end
/iKopter/trunk/Classes/Views/ChannelsViewController.m
0,0 → 1,159
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "ChannelsViewController.h"
#import "ChannelsViewCell.h"
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
#define kAnalogLabelFile @"AnalogLables.plist"
 
@interface ChannelsViewController (Private)
- (void) channelsValueNotification:(NSNotification *)aNotification;
- (void) requestChannelData;
@end
 
// ///////////////////////////////////////////////////////////////////////////////
 
@implementation ChannelsViewController
 
@synthesize updateTimer = _updateTimer;
 
#pragma mark -
#pragma mark View lifecycle
 
- (void) viewDidLoad {
[super viewDidLoad];
}
 
- (void) viewWillAppear:(BOOL)animated {
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(channelsValueNotification:)
name:MKChannelValuesNotification
object:nil];
 
[super viewWillAppear:animated];
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(requestChannelData)
userInfo:nil
repeats:YES];
[self requestChannelData];
}
 
- (void) viewDidDisappear:(BOOL)animated {
 
[self.updateTimer invalidate];
self.updateTimer=nil;
[_updateTimer release];
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
 
[super viewDidDisappear:animated];
}
 
#pragma mark -
#pragma mark Memory management
 
- (void) didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
 
- (void) viewDidUnload {
}
 
- (void) dealloc {
[super dealloc];
}
 
// ////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
- (void) requestChannelData {
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
NSData * data = [NSData dataWithCommand:MKCommandChannelsValueRequest
forAddress:MKAddressFC
payloadWithBytes:NULL
length:0];
 
[cCtrl sendRequest:data];
}
 
- (void) channelsValueNotification:(NSNotification *)aNotification {
NSData* data = [[aNotification userInfo] objectForKey:kMKDataKeyChannels];
if([data length]>=sizeof(channelValues))
memcpy(channelValues, [data bytes], sizeof(channelValues));
[self.tableView reloadData];
}
 
 
#pragma mark -
#pragma mark Table view data source
 
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
 
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return sizeof(channelValues);
}
 
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * CellIdentifier = @"ChannelsTableCell";
 
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
if (cell == nil) {
cell = [[[ChannelsViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
 
cell.textLabel.text = [NSString stringWithFormat:@"Channel %d",indexPath.row];
[(ChannelsViewCell*)cell setChannelValue:channelValues[indexPath.row]];
cell.detailTextLabel.hidden = YES;//.text = [NSString stringWithFormat:@"%d",channelValues[indexPath.row]];
 
return cell;
}
 
#pragma mark -
#pragma mark Table view delegate
 
- (NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
 
@end
 
/iKopter/trunk/Classes/Views/EngineTestSliderCell.h
0,0 → 1,36
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
 
@interface EngineTestSliderCell : UITableViewCell {
UISlider *valueSlider;
}
 
@property (nonatomic, retain) UISlider *valueSlider;
 
- (void)slideAction;
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier;
 
@end
/iKopter/trunk/Classes/Views/EngineTestSliderCell.m
0,0 → 1,71
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "EngineTestSliderCell.h"
#import "InAppSettingsConstants.h"
 
 
#define kUISliderWidth 160.0
#define kUISliderHeight 24.0
 
@implementation EngineTestSliderCell
 
@synthesize valueSlider;
 
- (void)slideAction{
}
 
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
if(self) {
 
CGRect frame = CGRectMake(126.0, 20.0, kUISliderWidth, kUISliderHeight);
valueSlider = [[UISlider alloc] initWithFrame:frame];
 
self.valueSlider.minimumValue = 0;
self.valueSlider.maximumValue = 1;
self.valueSlider.value = 0;
CGRect valueSliderFrame = self.valueSlider.frame;
valueSliderFrame.origin.y = (CGFloat)round((self.contentView.frame.size.height*0.5f)-(valueSliderFrame.size.height*0.5f));
valueSliderFrame.origin.x = 85;
valueSliderFrame.size.width = InAppSettingsScreenWidth-(InAppSettingsTotalTablePadding+InAppSettingsCellPadding+85);
self.valueSlider.frame = valueSliderFrame;
[self.valueSlider addTarget:self action:@selector(slideAction) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.valueSlider];
 
self.detailTextLabel.hidden = YES;
}
return self;
}
 
- (void)dealloc{
[valueSlider release];
[super dealloc];
}
 
@end
/iKopter/trunk/Classes/Views/EngineTestViewController.h
0,0 → 1,36
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
#import <UIKit/UIKit.h>
 
@class EngineValues;
 
@interface EngineTestViewController : UITableViewController {
 
EngineValues* engineValues;
UISegmentedControl* segment;
}
 
- (IBAction) changeDevice;
 
@end
/iKopter/trunk/Classes/Views/EngineTestViewController.m
0,0 → 1,189
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
#import "EngineValues.h"
#import "EngineTestViewController.h"
#import "EngineTestSliderCell.h"
#import "TTCorePreprocessorMacros.h"
 
 
//////////////////////////////////////////////////////////////////////////////////////////////
 
@interface EngineTestViewController ()
- (IBAction)engineValueChangedForAllAction:(UISlider *)sender;
- (IBAction)engineValueChangedAction:(UISlider *)sender;
@end
 
//////////////////////////////////////////////////////////////////////////////////////////////
@implementation EngineTestViewController
 
#pragma mark -
#pragma mark View lifecycle
 
 
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem* spacer;
spacer = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil] autorelease];
NSArray* segmentItems = [NSArray arrayWithObjects:@"Quadro",@"Hexa",@"Okto",nil];
segment = [[UISegmentedControl alloc] initWithItems:segmentItems];
segment.segmentedControlStyle=UISegmentedControlStyleBar;
[segment addTarget:self
action:@selector(changeDevice)
forControlEvents:UIControlEventValueChanged];
UIBarButtonItem* segmentButton;
segmentButton = [[[UIBarButtonItem alloc]
initWithCustomView:segment] autorelease];
[self setToolbarItems:[NSArray arrayWithObjects:spacer,segmentButton,spacer,nil]];
segment.selectedSegmentIndex=0;
}
 
 
//////////////////////////////////////////////////////////////////////////////////////////////
 
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
engineValues = [[EngineValues alloc] init];
}
 
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[engineValues start];
}
 
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[engineValues stop];
TT_RELEASE_SAFELY(engineValues);
}
 
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Table view data source
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 2;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section==0)
return 1;
// Return the number of rows in the section.
static NSInteger numberOfRows[3]={4,6,8};
return numberOfRows[segment.selectedSegmentIndex];
}
 
- (IBAction) changeDevice {
[self.tableView reloadData];
}
 
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"MotorTestSliderCell";
EngineTestSliderCell *cell = (EngineTestSliderCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[EngineTestSliderCell alloc] initWithReuseIdentifier:CellIdentifier] autorelease];
}
if(indexPath.section==0 ) {
cell.textLabel.text = [NSString stringWithFormat:@"All", [indexPath row]];
[cell.valueSlider addTarget:self
action:@selector(engineValueChangedForAllAction:)
forControlEvents:UIControlEventValueChanged ];
}
else {
cell.textLabel.text = [NSString stringWithFormat:@"Motor %d", [indexPath row]+1];
cell.valueSlider.tag = indexPath.row;
[cell.valueSlider addTarget:self
action:@selector(engineValueChangedAction:)
forControlEvents:UIControlEventValueChanged];
}
return cell;
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
 
- (IBAction)engineValueChangedForAllAction:(UISlider *)sender {
[engineValues setValueForAllEngines:(uint8_t)(sender.value*255.0)];
}
 
- (IBAction)engineValueChangedAction:(UISlider *)sender {
NSInteger theEngine=sender.tag;
[engineValues setValueForEngine:theEngine value:(uint8_t)(sender.value*255.0)];
}
 
 
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Table view delegate
 
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return nil;
}
 
//////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Memory management
 
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
 
- (void)viewDidUnload {
TT_RELEASE_SAFELY(segment);
}
 
 
- (void)dealloc {
TT_RELEASE_SAFELY(engineValues);
[super dealloc];
}
 
 
@end
 
/iKopter/trunk/Classes/Views/EngineValues.h
0,0 → 1,42
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
#import <Foundation/Foundation.h>
 
 
@interface EngineValues : NSObject {
 
uint8_t values[16];
NSTimer* updateTimer;
 
}
 
-(void)start;
-(void)stop;
-(void)setValueForEngine:(NSInteger)theEngine value:(uint8_t)newValue;
-(void)setValueForAllEngines:(uint8_t)newValue;
 
-(uint8_t) valueAtIndexPath:(NSIndexPath *)indexPath;
-(uint8_t) valueForEngine:(NSInteger)theEngine;
 
@end
/iKopter/trunk/Classes/Views/EngineValues.m
0,0 → 1,113
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "EngineValues.h"
#import "TTCorePreprocessorMacros.h"
 
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
@interface EngineValues ()
 
- (IBAction) writeValues;
 
@end
 
@implementation EngineValues
 
- (id) init
{
self = [super init];
if (self != nil) {
[self setValueForAllEngines:0];
[self writeValues];
}
return self;
}
 
- (void) dealloc
{
[self setValueForAllEngines:0];
[self writeValues];
DLog(@"dealloc");
[super dealloc];
}
 
-(void)start {
[self stop];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(writeValues)
userInfo:nil
repeats:YES];
}
 
-(void)stop {
TT_INVALIDATE_TIMER(updateTimer);
}
 
-(void)setValueForEngine:(NSInteger)theEngine value:(uint8_t)newValue {
if (theEngine<0 || theEngine>=16 )
return;
DLog("New value for engine %d = %d",theEngine,newValue);
values[theEngine]=newValue;
}
 
-(void)setValueForAllEngines:(uint8_t)newValue {
DLog("New value for all %d",newValue);
memset(values,newValue,sizeof(values));
}
 
-(uint8_t) valueAtIndexPath:(NSIndexPath *)indexPath {
return [self valueForEngine:indexPath.row];
}
 
-(uint8_t) valueForEngine:(NSInteger)theEngine {
if (theEngine<0 || theEngine>=16 )
return 0;
return values[theEngine];
}
 
- (IBAction) writeValues {
MKConnectionController* cCtrl=[MKConnectionController sharedMKConnectionController];
DLog("values e[0] = %d",values[0]);
NSData * data = [NSData dataWithCommand:MKCommandEngineTestRequest
forAddress:MKAddressAll
payloadWithBytes:values
length:16];
[cCtrl sendRequest:data];
}
 
@end
/iKopter/trunk/Classes/Views/LcdViewController.h
0,0 → 1,47
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import <UIKit/UIKit.h>
 
@interface LcdViewController : UIViewController {
UILabel * label;
int lcdCount;
UISegmentedControl* segment;
CGPoint gestureStartPoint;
BOOL canProcessNextGesture;
}
 
@property (nonatomic, retain) IBOutlet UILabel * label;
@property (nonatomic, retain) IBOutlet UISegmentedControl* segment;
 
@property CGPoint gestureStartPoint;
 
- (IBAction) nextScreen;
- (IBAction) prevScreen;
- (IBAction) changeDevice;
 
 
@end
/iKopter/trunk/Classes/Views/LcdViewController.m
0,0 → 1,238
// ///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, Frank Blumenberg
//
// See License.txt for complete licensing and attribution information.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// ///////////////////////////////////////////////////////////////////////////////
 
#import "LcdViewController.h"
#import "MKConnectionController.h"
#import "NSData+MKCommandEncode.h"
#import "MKDatatypes.h"
#import "MKDataConstants.h"
 
@implementation LcdViewController
 
@synthesize label;
@synthesize segment;
@synthesize gestureStartPoint;
 
- (void) viewDidLoad {
label.text = @"Not connected\r\nNo data\r\n\r\n";
lcdCount = 0;
[super viewDidLoad];
}
 
#pragma mark -
#pragma mark UIViewController delegate methods
 
 
 
- (void) updateSegment {
if ([[MKConnectionController sharedMKConnectionController] hasNaviCtrl]) {
[segment setEnabled:YES forSegmentAtIndex:0];
[segment setEnabled:YES forSegmentAtIndex:1];
[segment setEnabled:YES forSegmentAtIndex:2];
}
else {
[segment setEnabled:NO forSegmentAtIndex:0];
[segment setEnabled:YES forSegmentAtIndex:1];
[segment setEnabled:NO forSegmentAtIndex:2];
}
 
MKAddress currentDevice=[MKConnectionController sharedMKConnectionController].currentDevice;
switch (currentDevice) {
case MKAddressNC:
segment.selectedSegmentIndex=0;
break;
case MKAddressFC:
segment.selectedSegmentIndex=1;
break;
case MKAddressMK3MAg:
segment.selectedSegmentIndex=2;
break;
default:
break;
}
}
 
// called after this controller's view will appear
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setToolbarHidden:YES animated:NO];
 
// for aesthetic reasons (the background is black), make the nav bar black for this particular page
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
// match the status bar with the nav bar
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
 
[[MKConnectionController sharedMKConnectionController] activateNaviCtrl];
[self updateSegment];
}
 
- (void) viewDidAppear:(BOOL)animated {
canProcessNextGesture=YES;
 
// for aesthetic reasons (the background is black), make the nav bar black for this particular page
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
// match the status bar with the nav bar
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackOpaque;
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
 
[nc addObserver:self
selector:@selector(lcdNotification:)
name:MKLcdNotification
object:nil];
 
[self performSelector:@selector(sendMenuRefreshRequest) withObject:self afterDelay:0.1];
}
 
- (void) viewWillDisappear:(BOOL)animated {
NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
 
[nc removeObserver:self];
 
// restore the nav bar and status bar color to default
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
}
 
 
- (void) sendMenuRequestForKeys:(uint8_t)keys;
{
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
uint8_t lcdReq[2];
 
lcdReq[0] = keys;
lcdReq[1] = 50;
 
NSData * data = [NSData dataWithCommand:MKCommandLcdRequest
forAddress:MKAddressFC
payloadWithBytes:lcdReq
length:2];
 
[cCtrl sendRequest:data];
}
 
- (void) sendMenuRefreshRequest {
[self sendMenuRequestForKeys:0xFF];
}
 
- (void) lcdNotification:(NSNotification *)aNotification {
NSArray * mr = [[aNotification userInfo] objectForKey:kMKDataKeyMenuRows];
 
label.text = [mr componentsJoinedByString:@"\r\n"];
if (lcdCount++ > 4 ) {
[self sendMenuRefreshRequest];
lcdCount = 0;
}
}
 
- (IBAction) changeDevice {
[[MKConnectionController sharedMKConnectionController] activateNaviCtrl];
 
switch (segment.selectedSegmentIndex) {
case 1:
[[MKConnectionController sharedMKConnectionController] activateFlightCtrl];
break;
case 2:
[[MKConnectionController sharedMKConnectionController] activateMK3MAG];
break;
}
 
MKConnectionController * cCtrl = [MKConnectionController sharedMKConnectionController];
DLog(@"Device set to %d", cCtrl.currentDevice);
 
[self performSelector:@selector(sendMenuRefreshRequest) withObject:self afterDelay:0.1];
}
 
- (IBAction) nextScreen {
[self sendMenuRequestForKeys:0xFD];
}
 
- (IBAction) prevScreen {
[self sendMenuRequestForKeys:0xFE];
}
 
- (void) didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
 
- (void) viewDidUnload {
[super viewDidUnload];
}
 
- (void) dealloc {
[super dealloc];
}
 
#pragma mark -
 
#define kMinimumGestureLength 25
#define kMaximumVariance 5
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (!canProcessNextGesture)
return;
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = (gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = (gestureStartPoint.y - currentPosition.y);
BOOL leftToRight=(deltaX<0);
NSLog(@"%f",deltaX);
deltaX = fabsf(deltaX);
deltaY = fabsf(deltaY);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
if (leftToRight)
[self prevScreen];
else
[self nextScreen];
canProcessNextGesture=NO;
}
else if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance){
}
}
 
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
canProcessNextGesture=YES;
}
 
@end
/iKopter/trunk/Classes/Views/LcdViewController.xib
0,0 → 1,720
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1024</int>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">117</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="1"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBProxyObject" id="372490531">