Sunday, August 02, 2009

Pos .Net Series, Post #2 – Ugly Interfaces

This is the second post in a series on my initial experiences with using Microsoft Pos .Net. The first post can be found here. A complete list can be found here.

One of the problems with Pos .Net is the interfaces on some of the classes are quite ugly… now before I go any further I should state this probably isn’t Microsoft’s fault. Since Pos .Net is an implementation of the UPOS standard, it’s highly likely the ugliness of the interfaces comes from UPOS itself.

I can understand why Microsoft implemented the UPOS standard, but it’s a shame they didn’t take the opportunity to do something new and better. Possibly they could have provided separate classes for each device type, one that was UPOS compliant and one that was an improved version… but that would probably have prevented the new classes working with OPOS service objects, and then you get a chicken and egg situation with regards to device support, which I guess is why they didn’t. Yet another instance of technology not moving forward because of existing deployments.

There are two areas I’ve dealt with that particularly trouble me;


The common interface for opening, claiming, enabling devices and receiving status change notifications.


The PosPrinter interface (used for receipt and slip printers).

 

Common Interface Issues

I have two problems with this interface, and neither of them are major. The first is the process required to access a device seems to be unnecessarily complicated, and poorly documented as to what’s allowed under which conditions.

The typical sequence for accessing a device is;

  1. Open the device… this allows you to access some of the methods and properties of the device, but not all of them. There is no clear, centralised documentation about what is and isn’t allowed before and after open for any particular device class (I suspect it’s service object specific). Typically, after you call Open() you can access the capability properties (properties that start with the prefix Cap and determine whether or not a device is capable of a particular feature).
  2. Claim the device… this is designed as a sort of locking mechanism to avoid two programs trying to access the same device at the same time.
  3. Enable the device… usually by setting the DeviceEnabled property to true.
  4. On some input devices, like barcode scanners you also need to set the DataEventEnabled property to true.

When you’re finished with the device you generally do the reverse, DeviceEnabled = false, Release(), and sometimes Close() although typically you’d do that when your application was shutting down or you were otherwise permanently done with that device object.

So, what’s my problem with this ?

First, do we really need both  DeviceEnabled and DataEventEnabled properties ? I’m sure there’s some case where you do which is why there were implemented, but I just can’t see it… if I don’t want input from the device I disable it, and re-enable it again later, and why if I’ve claimed the device would I want to disable it so I couldn’t do anything with it at all ?

A bigger problem though is that Claim() and Release() are inconsistent. For some classes of devices, such as PosPrinters and CashDrawers you MUST claim them before you try to print, open the drawer etc. which is fair enough. KeyLocks, and some other device classes, do not (generally) require Claim to be called.

If you attempt to call Claim for a device that doesn’t support exclusive use you get an exception. There doesn’t appear to be any way to know whether a device requires being claimed or not either. Many of the device classes have a property called IsExclusiveUseDevice, but it is declared as protected and therefore not accessible from application code. Even if you could read it, the property is on each device interface (PosPrinter, KeyLock etc) and not a common interface. This means you’d have to cast any reference to the device to it’s most derived interface before you could check it.

So you either have to assume some device classes like KeyLocks don’t require Claim and Release, or you have to call them anyway and catch the exception. This seems like an unnecessary complication. If you assume a particular class of device will never be exclusive, then your code won’t work with any service object/device of that class that does require exclusive access. In the other case, your code will often throw and catch exceptions needlessly and hurt performance.

Why does it work like this ? If claim is meaningless to, say, a KeyLock (which makes sense), then why does it throw an exception instead of doing nothing ? It’s not like trapping that exception is really useful, and if there’s any chance of the device not being exclusive then you need to write your code so it handles non-exclusive devices anyway.

Worse still, I’m pretty sure the actual behaviour of the Claim method is specific to the service object being used… you may find that some KeyLock service objects don’t throw an exception when Claim is called and others do. Alterations in behaviour like that completely break the device independence we’re striving for.

The second problem I have with the common interfaces is around status and error handling. Each device has CapStatus and CapPowerReporting properties which return true or false (or an enum value in the case of CapPowerReporting) depending on whether or not the device is capable or reporting it’s status. They also have an event called StatusUpdateEvent which is raised when the device signals a change such as the cover being opened on the printer, paper running out or the cash drawer being opened or closed.

This is all good, it provides a nice easy way of getting notified of changes in state to the device so you can display errors or warnings, or keep track of the state so you know whether it’s safe to use the device before you start calling methods. The problem is you don’t get status events until after you set DeviceEnabled = true, which you can’t do until after you Claim the device (if claim is required).

My first complaint here is that I don’t see why I shouldn’t be able to be notified of the device status changing when I don’t have exclusive access. After all, that’s exactly how the KeyLock device class works. However, I can understand there might be technical reasons for doing so, like managing serial connections etc. inside the service object. It seems like these are issues that could be resolved, but might complicate development of service objects, so I’ll move along.

The really ugly bit is that you (usually) can’t claim a device that is already in an error state… so if you have a PosPrinter claimed and the cover is opened you get told via an event, but if you try to Claim a PosPrinter that has it’s cover open you get an error. The quality of the information in the error is again specific to the service object being used. One (legacy OPOS) service object I have just throws an exception stating the requested operation was illegal for the device’s current state and doesn’t return any extended information about what the state is… so I can’t tell the user what they need to fix.

In addition to a lack of information in the exception, there is now a big inconsistency between the way status errors are handled and that either leads to duplicated code, or if you’re tidy a bunch of re-useable methods for handling status conditions that are called from different places with as much data as is available at the time. This is not a cool design.

PosPrinter Interface Issues

The PosPrinter interface’s biggest issue is inconsistency. I was shocked to find the PosPrinter class still used escape sequences to format text… it seemed so archaic. Luckily, someone had the foresight to mandate a universal set of escape codes for use in UPOS standard (and therefore in Pos .Net). These can be found in the Pos .Net SDK documentation for the PosPrinter class.

I could get over using escape codes, especially since the universal ones do away with the issue of escape codes being inconsistent among even the same vendors models, but they aren’t used for everything !

If you want to make text bold, underlined, italicised, set alignment for text etc. you use an escape code. If you want to print a bitmap, set rotation for the print job or print a barcode you have to call methods on the object. Surely it would have been better to either have methods and properties OR escape codes for everything instead of a mix of the two.

The most offensive instance of this is around bitmap printing. There are three methods for dealing with bitmaps; PrintBitmap, PrintMemoryBitmap and SetBitmap.

Print bitmap prints a bitmap given a file name and a few other values about how the image should be printed such as alignment and size. I’d just like to point out as an aside this is yet another inconsistency… you set alignment for text by sending an escape code but the alignment for an image is passed on the method call to print the bitmap.

SetBitmap (we’ll get to PrintMemoryBitmap in a moment) takes an integer that specifies the ‘image number’ to store the image as, and a file name for the image to load. Note that  you cannot set an image without having it as a disk file, which is extremely inconvenient for me, especially as the file I pass in isn’t consistently free for deletion after the call to SetBitmap. In any case, SetBitmap should ‘store’ the bitmap for printing later (you’d assume in the printers nvram if it has any, but you wouldn’t always be right).

So given that, you’d assume that PrintMemoryBitmap would print an image previously saved to a memory location using SetBitmap, right ?

Wrong.

No, instead PrintMemoryBitmap prints an image passed to it via a System.Drawing.Image class instead of a file name… there is no method to print a bitmap you stored with SetBitmap – YOU HAVE TO USE AN ESCAPE CODE AGAIN !

That’s right, you set a bitmap with a method call and print it with an escape code. Geez, whoever came up with that is genius.

Also, In case you missed it; PrintBitmap has two versions, one that takes a file name and one that takes an image object, but SetBitmap has only  a version that takes a file name.

Another complaint is that PrintBitmap and PrintMemoryBitmap aren’t overloads, since they really do the same thing with different arguments. I guess that’s because the UPOS specification is language independent and not all languages support overloading, but it’s still a shame, and PrintMemoryBitmap isn’t a good name anyway.

While we’re talking about the PosPrinter class and bitmaps I should mention SetBitmap isn’t consistent in it’s purpose, again it changes with the service object being used, and local configuration.

With the Epson TM-T88III and it’s service object, SetBitmap seems to cache the image somewhere inside the service object itself, I can turn the printer on or off, connect or disconnect it and the correct bitmap will still print when I send the escape sequence. If I close the PosPrinter object, or shutdown my application however the bitmap I stored is lost… so I must set the bitmap on every start-up AND each time the image changes in my configuration database rather than just the latter.

With our new Epson TM-T88IV this still seems to be the default, unless you manually configure the device with the Epson SetupPos application provided in the Epson ADK; in this case when you setup the printer you get a checkbox (off by default) that says save images from SetBitmap in nvram. This is great, but requires the service object to be setup this way on each machine. What’s more, I can’t see anyway to easily set this programmatically.

The final problem I have is the escape code set for PosPrinters is deficient. I’ll explain how it works, but to understand why it’s a problem for me see this previous post on how we print receipts.

The escape codes provided allow you to turn on printing bold, italics, underline etc. but not turn them off. You can sort of turn off underline by specifying the underline width/count to be 0 (although some service objects might throw an exception in this case)… but you cannot disable bold. Even sending the bold command a second time won’t toggle it off.

You can ‘reset the print format’ by sending the ‘normal’ escape command, but this resets all formatting, i.e if you are printing centred, bold, and underlined, the ‘normal’ command will reset the you to non-bold, non-underlined, left aligned.

Additionally, the print format state is resent after every call to PrintNormal, PrintTwoNormal etc. so if you did this;

      posPrinter.PrintNormal(PrinterStation.Receipt, _BoldEscapeCommand);
      posPrinter.PrintNormal(PrinterStation.Receipt, "I want this text to be bold.");


You’ll get ordinary, non-bold text printed, as the bold was disabled immediately after the return from the PrintNormal method that sent the bold command itself.



This isn’t insurmountable, but it does complicate the code to print well formatted documents, as does not having everything as either an escape code or method call in the first place.



Think about it… if you’re processing a string that describes how to print your document you must queue up all the text and escape codes to be printed until you’re asked to print a bitmap, barcode etc. then you must stop, print what you’ve got, then call PrintBitmap/Barcode etc. then rebuild your print format from a stack of previously parsed format commands you’ve already processed.





Other Issues & Conclusion







The CheckScanner is another device with an odd interface, it works, but it’s kind of clumsy which is why they need a huge block of bullet points on the CheckScanner class documentation page to explain how to use it. It’s not intuitive, or consistent with other device types especially around when the DataEvent is raised.



There are several issues with the CheckScanner class but the worst one is it seems to use properties in place of event arguments or method parameters. For example the RetrieveMemory method (which is a bad name anyway) retrieves an image from the scanner’s memory using the type of value specified in the ‘by’ parameter, but you have to set a property on the class to give it the actual value to search by. For example, if you’ve said find by FileIndex, then you have to set the FileIndex property before you call this method.



Also, there doesn’t appear to be anyway to enumerate the images already in the scanner’s memory, something which is clearly frustrating someone.



Lack of enum use is another problem that occurs on all the device interfaces to some extent. Enums are used for things like cheque type and encryption methods on devices that support these settings, but not for status, error codes and other values.



Instead of enums you get a list of constants, thankfully with a common prefix such as Status or Error, which list the possible values. Of course this means intellisense doesn’t work as well, you can’t use the switch snippet provided with C#, and if you’re debugging some code and you get one of these values you have to manually inspect the value of each constant to figure out what the data means which is not a great debugging experience.



There may well be issues with other interfaces or area’s of the system, but I’ve only worked with a few so far and most of the others have seemed ok. These are just some issues to be aware of.



 




Technorati Tags: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,





Windows Live Tags: Series,Post,Microsoft,problems,implementation,UPOS,version,system,OPOS,objects,instance,technology,interface,devices,status,PosPrinter,receipt,Common,sequence,Open,documentation,Claim,mechanism,Enable,Release,Close,PosPrinters,CashDrawers,KeyLocks,fact,exception,CapClaim,code,Often,error,complication,KeyLock,IClaimable,behaviour,method,CapStatus,event,StatusUpdateEvent,printer,paper,cash,events,complaint,development,legacy,operation,user,addition,inconsistency,places,data,text,alignment,rotation,PrintBitmap,PrintMemoryBitmap,SetBitmap,Print,images,size,image,moment,integer,Note,disk,deletion,memory,location,Wrong,ESCAPE,AGAIN,Geez,genius,Another,arguments,specification,language,purpose,Epson,cache,configuration,database,SetupPos,setup,machine,mode,administrator,receipts,width,PrintNormal,PrintTwoNormal,PrinterStation,_BoldEscapeCommand,insurmountable,documents,Think,Barcode,commands,Interfaces,areas,notifications,printers,methods,errors,sequences,codes,vendors,languages,whether,drawer,bitmap,bitmaps,nvram,itself







WordPress Tags: Series,Post,Microsoft,problems,implementation,UPOS,version,system,OPOS,objects,instance,technology,interface,devices,status,PosPrinter,receipt,Common,sequence,Open,documentation,Claim,mechanism,Enable,Release,Close,PosPrinters,CashDrawers,KeyLocks,fact,exception,CapClaim,code,Often,error,complication,KeyLock,IClaimable,behaviour,method,CapStatus,event,StatusUpdateEvent,printer,paper,cash,events,complaint,development,legacy,operation,user,addition,inconsistency,places,data,text,alignment,rotation,PrintBitmap,PrintMemoryBitmap,SetBitmap,Print,images,size,image,moment,integer,Note,disk,deletion,memory,location,Wrong,ESCAPE,AGAIN,Geez,genius,Another,arguments,specification,language,purpose,Epson,cache,configuration,database,SetupPos,setup,machine,mode,administrator,receipts,width,PrintNormal,PrintTwoNormal,PrinterStation,_BoldEscapeCommand,insurmountable,documents,Think,Barcode,commands,Interfaces,areas,notifications,printers,methods,errors,sequences,codes,vendors,languages,whether,drawer,bitmap,bitmaps,nvram,itself







Blogger Labels: Series,Post,Microsoft,problems,implementation,UPOS,version,system,OPOS,objects,instance,technology,interface,devices,status,PosPrinter,receipt,Common,sequence,Open,documentation,Claim,mechanism,Enable,Release,Close,PosPrinters,CashDrawers,KeyLocks,fact,exception,CapClaim,code,Often,error,complication,KeyLock,IClaimable,behaviour,method,CapStatus,event,StatusUpdateEvent,printer,paper,cash,events,complaint,development,legacy,operation,user,addition,inconsistency,places,data,text,alignment,rotation,PrintBitmap,PrintMemoryBitmap,SetBitmap,Print,images,size,image,moment,integer,Note,disk,deletion,memory,location,Wrong,ESCAPE,AGAIN,Geez,genius,Another,arguments,specification,language,purpose,Epson,cache,configuration,database,SetupPos,setup,machine,mode,administrator,receipts,width,PrintNormal,PrintTwoNormal,PrinterStation,_BoldEscapeCommand,insurmountable,documents,Think,Barcode,commands,Interfaces,areas,notifications,printers,methods,errors,sequences,codes,vendors,languages,whether,drawer,bitmap,bitmaps,nvram,itself

2 comments:

  1. Yort;

    Gret post very informative on POS.NET!

    Thx 4 all the info;
    Catto

    ReplyDelete