Binding of Zebra SDK to Monotouch? - binding

I'm working on an iPad app in Monotouch (5.2.12). The app will be used with Zebra's mobile printers, so we got SDK from them (.a library and headers). So I went to read all the guides and tutorials and created a bindings project for it (just 2 connection classes). I was really excited to get it working quickly for basic text and label printing.
But we'll need to print PDFs. To do that, I need to bind more classes and I just cannot figure out how for 2 days now. Here's the general setup of the library:
ZebraPrinterConnection protocol is implemented by TcpPrinterConnection interface.
ZebraPrinterFactory interface is used to obtain an instance of ZebraPrinter protocol and requires ZebraPrinterConnection to be passed to it.
Here's the core of the bindings:
ZebraPrinterConnection
Header (.h)
#protocol ZebraPrinterConnection
- (BOOL) open;
- (void) close;
- (NSInteger) write:(NSData *)data error:(NSError **)error;
- (NSData *)read: (NSError**)error;
Binding (.cs)
[BaseType (typeof (NSObject))]
[Model]
interface ZebraPrinterConnection {
[Export ("open")]
bool Open();
[Export ("close")]
void Close();
[Export ("write:error:")]
int Write(NSData data, out NSError error);
[Export ("read:")]
NSData Read(out NSError error);
}
TcpPrinterConnection
Header (.h)
#interface TcpPrinterConnection : NSObject<ZebraPrinterConnection>
- (id)initWithAddress:(NSString *)anAddress andWithPort:(NSInteger)aPort;
Binding (.cs)
[BaseType (typeof(ZebraPrinterConnection))]
interface TcpPrinterConnection {
[Export ("initWithAddress:andWithPort:")]
IntPtr Constructor (string anAddress, int aPort);
}
ZebraPrinterFactory
Header (.h)
#interface ZebraPrinterFactory : NSObject
+(id<ZebraPrinter,NSObject>) getInstance:(id<ZebraPrinterConnection, NSObject>)
connection error:(NSError**)error
Binding (.cs)
[BaseType (typeof(NSObject))]
interface ZebraPrinterFactory {
[Static, Export ("getInstance:error:")]
ZebraPrinter getInstance(ZebraPrinterConnection connection, out NSError error);
}
The problem:
Note how ZebraPrinterFactory wants ZebraPrinterConnection to be passed to it, but only TcpPrinterConnection has an actual constructor.
If I try to use something like:
NSError err;
TcpPrinterConnection conn;
conn = new TcpPrinterConnection(ipAddress, port);
bool connectionOK = conn.Open ();
ZebraPrinter zPrinter = ZebraPrinterFactory.getInstance(conn, out err);
then I get a "System.InvalidCastException: Cannot cast from source type to destination type." at runtime...
It's a terrible feeling knowing that you ALMOST got it working, but not quite... How does one get around this?
UPDATE: OK, I removed the ZebraPrinterConnection class from the binding entirely, copying its methods into TcpPrinterConnection (as suggested by Jonathan). Still no luck (same exception). Then bound another class that has methods that expect ZebraPrinterConnection as a parameter and this one works smooth as silk.
Header (.h)
#interface SGD : NSObject {}
+(NSString*) GET: (NSString*) setting
withPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection
error:(NSError**)error;
Binding (.cs)
[BaseType (typeof (NSObject))]
interface SGD
{
[Static, Export ("GET:withPrinterConnection:error:")]
string Get (string setting, TcpPrinterConnection printerConnection, out NSError error);
}
I am starting to suspect the implementation of ZebraPrinterFactory class being root of the problem, now that other classes can be bound without any issues whatsoever. On the other hand, it might have something to do with the returned instance of ZebraPrinter class. Could it be that Mono cannot map ZebraPrinter to the thing being returned by the factory class?

I'm not familiar with MonoTouch, but know about the zebra apis. Hopefully some background of the way the sdk works will help with these mono mappings
So, the ZebraPrinterFactory is a simple factory which interrogates a printer connection to figure out which model of printer you have (ZPL or CPCL). The return type of the factory is an Interface, ZebraPrinter. The concrete type of a ZebraPrinter is an internal, non documented class, ZebraPrinterZpl or ZebraPrinterCpcl. Both of these concrete classes conform to the ZebraPrinter interface and can perform functions on a printer. We chose to do this to abstract away the need to know about printer languages. But if Mono needs to know about a concrete class to perform the mapping, you can cast the return to the concrete class ZebraPrinterCpcl or ZebraPrinterZpl (depending on your printer model, small mobile printers are probably cpcl, large desktops or tabletops are ZPL)
You can alternatively bypass the factory by instatiating a concrete type of printer by just calling its constructor (unfortunately, it is not documented, because the factory is the preferred way to do this) But it would be something like this:
ZebraPrinterCpcl myPrinter = new ZebraPrinterCpcl(conn);
//or using the base interface as the type... and you may need to pass in an NSError also, I forget now...
ZebraPrinter myPrinter = new ZebraPrinterCpcl(conn);
Hope this helps!

Instead of:
[BaseType (typeof(NSObject))]
interface TcpPrinterConnection : ZebraPrinterConnection {
Use:
[BaseType (typeof(ZebraPrinterConnection ))]
interface TcpPrinterConnection {
Then the binding project should setup your inheritance correctly. The BaseType attribute determines the base class, not actually inheriting the interface (I know it's a little weird).

Related

Ninject selecting parameterless constructor when using implicit self-binding

I am using Ninject version 3 in an MVVM-type scenario in a .NET WPF application. In a particular instance I am using a class to act as coordinator between the view and its view model, meaning the coordinator class is created first and the view and view model (along with other needed services) are injected into it.
I have bindings for the services, but I have not created explicit bindings for the view/view model classes, instead relying on Ninject's implicit self-binding since these are concrete types and not interfaces.
A conceptual version of this scenario in a console app is shown below:
class Program
{
static void Main(string[] args)
{
StandardKernel kernel = new StandardKernel();
kernel.Bind<IViewService>().To<ViewService>();
//kernel.Bind<View>().ToSelf();
//kernel.Bind<ViewModel>().ToSelf();
ViewCoordinator viewCoordinator = kernel.Get<ViewCoordinator>();
}
}
public class View
{
}
public class ViewModel
{
}
public interface IViewService
{
}
public class ViewService : IViewService
{
}
public class ViewCoordinator
{
public ViewCoordinator()
{
}
public ViewCoordinator(View view, ViewModel viewModel, IViewService viewService)
{
}
}
If you run this code as-is, the kernel.Get<> call will instantiate the ViewCoordinator class using the parameterless constructor instead of the one with the dependencies. However, if you remove the parameterless constructor, Ninject will successfully instantiate the class with the other constructor. This is surprising since Ninject will typically use the constructor with the most arguments that it can satisfy.
Clearly it can satisfy them all thanks to implicit self-binding. But if it doesn't have an explicit binding for one of the arguments it seems to first look for alternate constructors it can use before checking to see if it can use implicit self-binding. If you uncomment the explicit Bind<>().ToSelf() lines, the ViewController class will instantiate correctly even if the parameterless constructor is present.
I don't really want to have to add explicit self-bindings for all the views and view models that may need this (even though I know that burden can be lessened by using convention-based registration). Is this behavior by design? Is there any way to tell Ninject to check for implicit self-binding before checking for other usable constructors?
UPDATE
Based on cvbarros' answer I was able to get this to work by doing my own implementation of IConstructorScorer. Here's the changes I made to the existing code to get it to work:
using Ninject.Selection.Heuristics;
class Program
{
static void Main(string[] args)
{
StandardKernel kernel = new StandardKernel();
kernel.Components.RemoveAll<IConstructorScorer>();
kernel.Components.Add<IConstructorScorer, MyConstructorScorer>();
kernel.Bind<IViewService>().To<ViewService>();
ViewCoordinator viewCoordinator = kernel.Get<ViewCoordinator>();
}
}
using System.Collections;
using System.Linq;
using Ninject.Activation;
using Ninject.Planning.Targets;
using Ninject.Selection.Heuristics;
public class MyConstructorScorer : StandardConstructorScorer
{
protected override bool BindingExists(IContext context, ITarget target)
{
bool bindingExists = base.BindingExists(context, target);
if (!(bindingExists))
{
Type targetType = this.GetTargetType(target);
bindingExists = (
!targetType.IsInterface
&& !targetType.IsAbstract
&& !targetType.IsValueType
&& targetType != typeof(string)
&& !targetType.ContainsGenericParameters
);
}
return bindingExists;
}
private Type GetTargetType(ITarget target)
{
var targetType = target.Type;
if (targetType.IsArray)
{
targetType = targetType.GetElementType();
}
if (targetType.IsGenericType && targetType.GetInterfaces().Any(type => type == typeof(IEnumerable)))
{
targetType = targetType.GetGenericArguments()[0];
}
return targetType;
}
}
The new scorer just sees if a BindingExists call failed by overriding the BindingExists method and if so it checks to see if the type is implicitly self-bindable. If it is, it returns true which indicates to Ninject that there is a valid binding for that type.
The code making this check is copied from the SelfBindingResolver class in the Ninject source code. The GetTargetType code had to be copied from the StandardConstructorScorer since it's declared there as private instead of protected.
My application is now working correctly and so far I haven't seen any negative side effects from making this change. Although if anyone knows of any problems this could cause I would welcome further input.
By default, Ninject will use the constructor with most bindings available if and only if those bindings are defined (in your case they are implicit). Self-bindable types do not weight when selecting which constructor to use.
You can mark which constructor you want to use by applying the [Inject] attribute to it, this will ensure that constructor is selected.
If you don't want that, you can examine StandardConstructorScorer to see if that will fit your needs. If not, you can replace the IConstructorScorer component of the Kernel with your own implementation.

Monotouch binding - "Cannot cast from source type to destination type."

I am a newbie on Monotouch. Recently, I am working on a Monotouch binding project that binds a custom iOS framework that developed myself into a .NET framework library. I follow the instructions on Xamarin but currently I am having an issue that cannot be resolved. This is my code.
**HEADER FILE IN OBJECTIVE C**
*GRG.h*
#interface GRG: NSObject {}
// Shared instance
+ (GRG*) sharedG;
// Preference class
#property (nonatomic, readonly) GRGPreferences *preferences;
// Driver version
#property (readonly,copy) NSString* driverVersion;
// More parameters...
#end
*GRGPreferences.h*
#interface GRGPreferences : NSObject <GRGPreferencesProtocol>{}
// Enable DEBUG
#property BOOL debugEnabled;
// More parameters...
#end
*GRGPreferencesProtocol.h*
#protocol GRGPreferencesProtocol <NSObject>
// More parameters...
#end
I convert my header file into this
**API DEFINITION**
[BaseType (typeof (NSObject))]
interface GRG
{
[Static][Export("sharedG")]
GRG SharedG{ get; }
[Export("preferences")]
GRGPreferences Preferences{ get;}
[Export("driverVersion", ArgumentSemantic.Copy)]
string DriverVersion {get;}
}
[BaseType (typeof (GRGPreferencesProtocol))]
public interface GRGPreferences
{
[Export("debugEnabled")]
bool DebugEnabled{ get; set;}
}
[BaseType(typeof (NSObject))]
[Model]
public interface GRGPreferencesProtocol
{}
After that, I created a test app on mono to test the newly created library and get access to the values I created. However, I got an error.
Console.WriteLine(GRG.sharedG.DriverVersion);
- This works fine. It returns the proper value.
GRGPreferences pref = GRG.SharedG.Preferences;
- Error : "Cannot cast from source type to destination type."
Console.WriteLine(GRG.sharedG.Preferences.DebugEnabled);
- Error : "Cannot cast from source type to destination type."
Can anyone please help me?
From a quick look I think this is what you want:
[BaseType (typeof (NSObject))]
public interface GRGPreferences : GRGPreferencesProtocol {
Your GRGPreferences type inherits from NSObject while implementing the protocol you want.

How to define that a function returns an object when building binding for a iOS library

Building a wrapper for an iOS library. So far so good. Except in a method that returns an object. Method always returns NSObject instead. The same story is in delegate that has an object parameter .
For example,TCConnection is defined in Monotouch binding project as
//#interface TCConnection : NSObject
[BaseType (typeof(NSObject))]
public interface TCConnection
{
[ExportAttribute("state")]
int state {get;}
//...
}
class TCDevice is defined as follows
//#interface TCDevice : NSObject
[BaseType (typeof(NSObject))]
public interface TCDevice
{
[ExportAttribute("initWithCapabilityToken:delegate:")]
IntPtr Constructor(String token, TCDeviceDelegate d);
[Export("connect:delegate:")]
TCConnection Connect([NullAllowed] NSDictionary param, [NullAllowed] TCConnectionDelegate del);
}
Everything is compiled nicely to a dll.
When I use the dll in other project, I call
MyTCDeviceDelegate d=new MyTCDeviceDelegate();
String token="XXXXX";
TCDevice dev=new TCDevice(token, d);
TCConnection conn=dev.Connect(null,null);
The last line always throws Invalid Cast exception.It looks like the method returns NSObject.
What I am missing here?

Has anyone created a MonoTouch binding for the Nuance Dragon Mobile Speech SDK for iOS?

I have the Dragon Mobile SDK running nicely on Windows Phone 7 and I would like to get the equivalent functionality working for iOS. Since the SDK wraps the microphone, it's not really possible to use the .NET assemblies in my MonoTouch project (even if I did have the source). It appears that the best way to do this is to create a binding library (as Miguel describes here).
It sure seems like a lot of work though, and I would love to reuse as opposed to reinventing the wheel if someone's done it already...
Here are some more details for how I got this to work.
I downloaded the binding sample. You may be tempted to skip this step, but you really have to start with this project if you want to get this to work.
I created an objective-c library with Xcode (which I called SpeechKitLibrary) that has a dual purpose - one is to define the SpeechKitApplicationKey (which is an extern dependency that SpeechKit needs):
const unsigned char SpeechKitApplicationKey[] = {...};
and the other is to define a class which utilizes the SpeechKit framework, and links with it. (in Xcode, add the SpeechKit framework in the frameworks section of the project).
The .m file I wrote looks something like this... (you can figure out the .h file - super simple). I'm not 100% sure you need all of this, but I wanted to make sure the static archive library that came out of this step would import the right symbols. You may be able to avoid this somehow, but in my experiments I found that I needed to do something like this...
// the SpeechKitWrapper isn't actually used - rather, it is a way to exercise all the API's that
// the binding library needs from the SpeechKit framework, so that those can be linked into the generated .a file.
#implementation SpeechKitWrapper
#synthesize status;
- (id)initWithDelegate:(id <SKRecognizerDelegate>)delegate
{
self = [super init];
if (self) {
del = delegate;
[self setStatus:#"initializing"];
SpeechKit setupWithID:#"NMDPTRIAL_ogazitt20120220010133"
host:#"sandbox.nmdp.nuancemobility.net"
port:443
useSSL:NO
delegate:nil];
NSString *text = [NSString stringWithFormat:#"initialized. sessionid = %#", [SpeechKit sessionID]];
[self setStatus:text];
SKEarcon* earconStart = [SKEarcon earconWithName:#"beep.wav"];
[SpeechKit setEarcon:earconStart forType:SKStartRecordingEarconType];
voiceSearch = [[SKRecognizer alloc] initWithType:SKDictationRecognizerType
detection:SKLongEndOfSpeechDetection
language:#"en_US"
delegate:delegate];
text = [NSString stringWithFormat:#"recognizer connecting. sessionid = %#", [SpeechKit sessionID]];
[self setStatus:text];
}
return self;
}
#end
I then compiled/linked this static archive for the three different architectures - i386, arm6, and arm7. The Makefile in the BindingSample is the template for how to do this. But the net is that you get three libraries - libSpeechKitLibrary-{i386,arm6,arm7}.a. The makefile then creates a universal library (libSpeechKitLibraryUniversal.a) using the OSX lipo(1) tool.
Only now are you ready to create a binding library. You can reuse the AssemblyInfo.cs in the binding sample (which will show how to create an import on the universal library for all architectures - and will drive some compile flags)...
[assembly: LinkWith ("libSpeechKitLibraryUniversal.a", LinkTarget.Simulator | LinkTarget.ArmV6 | LinkTarget.ArmV7, ForceLoad = true)]
You compile the ApiDefinition.cs file with btouch as per the Makefile (I think I needed to repeat some of the info in StructsAndEnums.cs to make it work). Note - the only functionality I didn't get to work is the "SetEarcon" stuff - since this is an archive library and not a framework, I can't bundle a wav as a resource file... and I couldn't figure out how to get the SetEarcon method to accept a resource out of my app bundle.
using System;
using MonoTouch.Foundation;
namespace Nuance.SpeechKit
{
// SKEarcon.h
public enum SKEarconType
{
SKStartRecordingEarconType = 1,
SKStopRecordingEarconType = 2,
SKCancelRecordingEarconType = 3,
};
// SKRecognizer.h
public enum SKEndOfSpeechDetection
{
SKNoEndOfSpeechDetection = 1,
SKShortEndOfSpeechDetection = 2,
SKLongEndOfSpeechDetection = 3,
};
public static class SKRecognizerType
{
public static string SKDictationRecognizerType = "dictation";
public static string SKWebSearchRecognizerType = "websearch";
};
// SpeechKitErrors.h
public enum SpeechKitErrors
{
SKServerConnectionError = 1,
SKServerRetryError = 2,
SKRecognizerError = 3,
SKVocalizerError = 4,
SKCancelledError = 5,
};
// SKEarcon.h
[BaseType(typeof(NSObject))]
interface SKEarcon
{
[Export("initWithContentsOfFile:")]
IntPtr Constructor(string path);
[Static, Export("earconWithName:")]
SKEarcon FromName(string name);
}
// SKRecognition.h
[BaseType(typeof(NSObject))]
interface SKRecognition
{
[Export("results")]
string[] Results { get; }
[Export("scores")]
NSNumber[] Scores { get; }
[Export("suggestion")]
string Suggestion { get; }
[Export("firstResult")]
string FirstResult();
}
// SKRecognizer.h
[BaseType(typeof(NSObject))]
interface SKRecognizer
{
[Export("audioLevel")]
float AudioLevel { get; }
[Export ("initWithType:detection:language:delegate:")]
IntPtr Constructor (string type, SKEndOfSpeechDetection detection, string language, SKRecognizerDelegate del);
[Export("stopRecording")]
void StopRecording();
[Export("cancel")]
void Cancel();
/*
[Field ("SKSearchRecognizerType", "__Internal")]
NSString SKSearchRecognizerType { get; }
[Field ("SKDictationRecognizerType", "__Internal")]
NSString SKDictationRecognizerType { get; }
*/
}
[BaseType(typeof(NSObject))]
[Model]
interface SKRecognizerDelegate
{
[Export("recognizerDidBeginRecording:")]
void OnRecordingBegin (SKRecognizer recognizer);
[Export("recognizerDidFinishRecording:")]
void OnRecordingDone (SKRecognizer recognizer);
[Export("recognizer:didFinishWithResults:")]
[Abstract]
void OnResults (SKRecognizer recognizer, SKRecognition results);
[Export("recognizer:didFinishWithError:suggestion:")]
[Abstract]
void OnError (SKRecognizer recognizer, NSError error, string suggestion);
}
// speechkit.h
[BaseType(typeof(NSObject))]
interface SpeechKit
{
[Static, Export("setupWithID:host:port:useSSL:delegate:")]
void Initialize(string id, string host, int port, bool useSSL, [NullAllowed] SpeechKitDelegate del);
[Static, Export("destroy")]
void Destroy();
[Static, Export("sessionID")]
string GetSessionID();
[Static, Export("setEarcon:forType:")]
void SetEarcon(SKEarcon earcon, SKEarconType type);
}
[BaseType(typeof(NSObject))]
[Model]
interface SpeechKitDelegate
{
[Export("destroyed")]
void Destroyed();
}
[BaseType(typeof(NSObject))]
interface SpeechKitWrapper
{
[Export("initWithDelegate:")]
IntPtr Constructor(SKRecognizerDelegate del);
[Export("status")]
string Status { get; set; }
}
}
You now have an assembly that can be referenced by your monotouch application project. The important thing now is to remember to link with all the frameworks that are dependencies (not only SpeeckKit, but also SK's dependencies) - you do this by passing mtouch some additional arguments:
-gcc_flags "-F<insert_framework_path_here> -framework SpeechKit -framework SystemConfiguration -framework Security -framework AVFoundation -framework AudioToolbox"
That's all, folks! Hope this was helpful...
If anyone (kos or otherwise) gets the SetEarcon method to work, please post a solution :-)
Nuance's SDK Agreement is not permissive enough for anyone to even publish bindings for their iOS SDK for use with MonoTouch. But the library itself should work just fine.
That being said, the SDK has only a handful of types to map and would be fairly trivial to RE-do the work anyone else might have already done. You can check out how to bind assemblies using the reference guide here:
http://docs.xamarin.com/ios/advanced_topics/binding_objective-c_types
There's also a BindingSample project that helps users better understand how to bind native components using btouch:
https://github.com/xamarin/monotouch-samples/tree/master/BindingSample
Thanks again Anuj for your answer. I thought I'd leave a tip or two about how to do this. The binding library wasn't difficult to build (still tweaking it but it's not a difficult task).
The more obscure part was figuring out how to get the SpeechKit framework linked. The samples only show how to link a .a or .dylib. After spending a little time with the ld(1) man page on OSX, it looks like the correct ld (and therefore gcc) arguments for linking with a framework are the following:
-gcc_flags "-F<insert_framework_path_here> -framework SpeechKit"
You put this in a textbox in the project properties - under Build :: iPhone Build :: Additional mtouch arguments
Note that -L doesn't work because this isn't a library; also note that -force_load and -ObjC referenced here don't appear necessary because, again, this is a framework and not a library.

Best practices: Calling GetInstance method to instantiate object

Lets discuss one thing:
I have some simple interface:
public interface ICar
{
void StartEngine();
void StopEngine();
}
public interface IRadio
{
//doesn't matter
}
and some implementation:
public class SportCar : ICar
{
private IRadio radio;
public SportCar(IRadio radioObj)
{
radio = radioObj;
}
//all the rest goes here
}
also we have our StructureMap initialization code, and we calling it on Program initialization:
private void InitializeStructureMap()
{
ObjectFactory.Initialize(x=>
{
x.For<ICar>.Use<SportCar>();
x.For<IRadio>.Use<CarAudioSystem>();
});
}
And my question is: what is the best practice to instantiate SportCar? Is calling:
ObjectFactory.GetInstance<ICar>()
a good practice (now I don't now other way to resolve this)?
ObjectFactory.GetInstance is your starting point, that is what you use to resolve the first object in the hierarcy.
This is how i start my WinForms applications, the same technique should apply to WebForms, Windows Services and Console Applications:
var main = ObjectFactory.GetInstance<Main>();
Application.Run(main);
For ASP.NET MVC the framework allows you to register a factory that creates your controllers, but even in that factory you would call ObjectFactory.GetInstance to instanciate your controller.
As a side note:
When you do initialization, you don't explicitly need to map ICar to SportCar unless you have multiple ICar implementations, you can just do
x.Scan(a => { a.TheCallingAssembly(); a.WithDefaultConventions(); });
which wil map your interfaces with default implementations.

Resources