OPOS Printing hanging main application although printing done in secondary thread
Hi,
I've recently acquired an EPOS application and I'm having some problems with printing under OPOS hanging the main thread. The print is done be a secondary thread so it's effect should be relatively mimimal on the main UI thread.
In theory at least (if I do a threading.thread.sleep in the print thread for e.g. 10 seconds before it starts to print, the UI thread still functions for those 10 seconds) any OPOS printing is done in the secondary thread.
I've got 2 printers, a thermal using the normal Epson OPOS driver (PosLigne TRP-100 using Epson ADK V2.50) and a dot-matrix (Bixolon SRP-275C using Bixolon Opos Driver V4.1.1).
They behave differently (as is often the way with drivers). The Epson OPOS driver hangs the main app thread when printing, but throws an error if, e.g., the printer runs out of paper or the print door is opened mid-print). The Bixolon doesn't hang the main thread, but will not notice errors mid-print (it errors on paper-out at the start of printing, but not during).
I'm mainly concerned with the main app locking up while printing. The processor doesn't go to max so I'm open to clues why something running in an independant thread can stop the main thread, or really any other thoughts or ideas on what might be going wrong.
Many thanks
Mike
Answers
- Glad we could help... I take it setting the AsyncMode property to true stopped the main thread from being blocked ?
- Marked As Answer byMike Hoffman Sunday, August 16, 2009 8:14 PM
All Replies
- Are there other POS device open at the time?
I created a simple cash register application that supported a number of devices - Poll Display, MSR, bar code scanner, printing, cashdrawer. In Pos for .NET 1.1, if too many of the devices are open, the application slows to a crawl.
-Sean
www.sjjmicro.com / www.seanliming.com, Book Author - XP Embedded Advanced, XPe Supplemental Toolkit, WEPOS / POS for .NET Step-by-Step - Hi Sean,
I've removed all the other OPOS peripherals (Pole, MSR, CD etc) that would be needed in the real app so at the moment it's just the printers.
I can understand why the Bixilon works the way it does, once it does its checks it fires and forgets the print output (at a guess). But I'm baffled why a print job called from a secondary thread would prevent the main thread from functioning. I'm as convinced as I can be the secondary thread is independant of the main thread (e.g. the Thread.Sleep would halt the main thread if they were still attached) so it's all a mystery to me!
Cheers
Mike Instead of trying to pint from a second thread (it's possibly the service object may not be happy with that if it wasn't written to be multi-threaded), have you tried using the Async print feature built into OPOS (at least these are in Pos .Net so I assume they are in OPOS to as part of the UPOS spec) ? You can enable this ia the AsyncMode propery on the printer object I think. The Pos .Net SDK has quite good documentation on how to implement this.
It may be that if you use the internal threading model for OPOS/Pos .Net the service object will behave better, and I think you'll also get better error handling via the ErrorEvent event in case of paper out etc.
OPOS objects are typically COM objects and they have specific needs about the apartment threading model etc. so you may be running into an issue caused by your use of a secondary thread and the service objects threading apartment threading support, or even just that the service object isn't thread safe some how.
Also, IF you are using Pos .Net, are you passing in a synchronising object to the constructor of PosExplorer ? It may be the PosPrinter is using that to invoke back to the main thread, if so you could not pass it in and see if that fools it into staying on the secondary thead but you might end up having to manually invoke back to the main thread in events from any other devices created using that PosExplorer instance.
Sean, it's a big shame that having that many devices open slows the process/pc to a crawl, that's exactly the sort of thing a POS or Kiosk system would want to do and makes Pos .Net kind of useless if that's true. We have a PC in our office running our POS system that has a line display, cash drawer, receipt printer and MSR all loaded as OPOS/Pos .Net objects plus a Pos Keyboard and Barode scanner which just operate as normal keyboard devices and we haven't noticed a problem... did you determine a specific number of devices that was an issue or if it was a specific device/device combination ?- In my case, it is probably just bad programming when I first created the application by not doing the open, claim, function, release, close properly. You can access all the devices just don't leave them open all the time. This was for POS for .NET 1.11. When I did the proper synquencing with the devices, things ran smoother. There are lots of modes to play with as you point out.
-Sean
www.sjjmicro.com / www.seanliming.com, Book Author - XP Embedded Advanced, XPe Supplemental Toolkit, WEPOS / POS for .NET Step-by-Step - Thanks Sean, good to know... although in our case we (optionally, but by default) leave the devices opened and claimed all the time because there is such performance overhead in some service objects with the open, claim, enable, disable, release, close methods, and ours is usually the only software on our customers PC's requiring access to the devices.
Yort,
I kinda went down the Async printing route but without the service object. I'll win no awards for the code below, but in case it helps anyone here's a cut down version. This seems to work with both printer drivers and as I've got very tight deadlines, on occassions, the quickest way is the only way.
Imports Microsoft.PointOfService
Imports System.NetPublic Class clsPrinting
Public Structure OrderPrinterJob
Dim TextToPrint As String
Dim PrinterIndex As String
Dim PrintHeader As Boolean
Dim PrintFooter As Boolean
Dim JobType As OrderPrintJobType
Dim done As Boolean
Dim Failures As Integer
End StructurePublic Enum OrderPrintJobType
Kitchen = 0
Waiter = 1
Receipt = 2
End EnumPublic Shared OrderPrintJobs As New Generic.Queue(Of OrderPrinterJob)
Private Shared PumpThread As New Threading.Thread(AddressOf OrderPrintJobPump)Private Shared PrinterIdleEvent As Boolean
Private Shared PrinterErrorEvent As BooleanPublic Shared Sub StartPump()
PumpThread.IsBackground = True
PumpThread.Name = "Printing Thread"
PumpThread.Priority = Threading.ThreadPriority.Lowest
PumpThread.Start()
End SubPrivate Shared Sub OrderPrintJobPump()
While True
While OrderPrintJobs.Count > 0
Dim kpj = OrderPrintJobs.DequeueIf Not kpj.done Then PrintToOrderPrinterEx(kpj)
End WhileThreading.Thread.Sleep(5000)
End WhileEnd Sub
Private Shared Printers As New Generic.List(Of PosPrinter)
Private Shared PrintersLDN As New Generic.List(Of String)Private Shared Sub PrintToOrderPrinterEx(ByVal KPJob As clsPrinting.OrderPrinterJob)
PrintToOPOSPrinter(KPSetting, KPJob.TextToPrint, KPJob.PrintHeader, KPJob.PrintFooter)
End SubPrivate Shared Sub PrintToOPOSPrinter(ByVal PrinterName As String, ByVal Text As String, Optional ByVal PrintHeader As Boolean = False, Optional ByVal PrintFooter As Boolean = False)
Dim LDNIndex As Integer = 0
Dim IsReprint As BooleanFor Each printer In Printers
If PrinterName.ToUpper = PrintersLDN.Item(LDNIndex).ToUpper Then
Dim PrintError As Boolean = True
IsReprint = False
PrinterErrorEvent = FalseDo Until (PrintError = False)
With printer
Try
.Claim(1000)If .Claimed Then
AddHandler .ErrorEvent, AddressOf DeviceErrorEventHandler
AddHandler .OutputCompleteEvent, AddressOf OutputCompleteEventHandlerIsReprint = PrinterErrorEvent
PrinterIdleEvent = False
PrinterErrorEvent = FalseTry
.FlagWhenIdle = True
.DeviceEnabled = True
.AsyncMode = True.CharacterSet = 1252
If IsReprint = True Then .PrintNormal(2, "** REPRINT **" & vbCrLf & vbCrLf & vbCrLf)
.PrintNormal(2, Text)
.PrintNormal(2, vbCrLf & vbCrLf & vbCrLf & vbCrLf)
.CutPaper(100)For count As Integer = 1 To 1200
If PrinterIdleEvent = True Or PrinterErrorEvent = True Then Exit For
Threading.Thread.Sleep(50)
System.Windows.Forms.Application.DoEvents()
NextPrintError = False
If PrinterErrorEvent = True Then
If clsGlobal.ShowMessageWindow("Printer Error on " & PrinterName & ". Would you like to retry?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
PrintError = True
End IfEnd If
Finally
RemoveHandler .ErrorEvent, AddressOf DeviceErrorEventHandler
RemoveHandler .OutputCompleteEvent, AddressOf OutputCompleteEventHandler
End Try
Else
If clsGlobal.ShowMessageWindow("Printer Error on " & PrinterName & ". Would you like to retry?", MsgBoxStyle.YesNo) = MsgBoxResult.No Then
Exit Sub
End If
End IfCatch ex As Exception
If clsGlobal.ShowMessageWindow("Printer Error on " & PrinterName & ". Would you like to retry?", MsgBoxStyle.YesNo) = MsgBoxResult.No Then
Try
.DeviceEnabled = False
.Release()
Catch ex1 As Exception
End Try
Exit Sub
End If
End TryTry
.DeviceEnabled = False
.Release()
Catch ex As Exception
End TryEnd With
Loop
End IfLDNIndex += 1
Next
End SubPrivate Shared Sub DeviceErrorEventHandler(ByVal Sender As Object, ByVal e As DeviceErrorEventArgs)
PrinterErrorEvent = True
e.ErrorResponse = ErrorResponse.Clear
End SubPrivate Shared Sub OutputCompleteEventHandler(ByVal Sender As Object, ByVal e As OutputCompleteEventArgs)
PrinterIdleEvent = True
End SubEnd Class
Cheers
Mike- Glad we could help... I take it setting the AsyncMode property to true stopped the main thread from being blocked ?
- Marked As Answer byMike Hoffman Sunday, August 16, 2009 8:14 PM
- Hi Yort,
Yeah, AsyncMode coupled with the 'up to 60 seconds' delay and event seemed to work a treat.
Not sure why the 2 printers behaved so differently with AsyncMode off, but I'll put that on my 'to investagate when I've got any time at all to worry about it' list ; )
You've probably guessed I'm very new to OPOS but it looks like I'll be here for a while, so hopefully I can help you out at some point. Till then I've marked your entry as the answer.
Take it easy.
Thanks again Sean for your help also :-)
Cheers
Mike - Cool, thanks for that.
It's distressing but very common that different service objects have different behaviour, breaking the device independence of the OPOS/Pos .Net implementations. I'm not sure why you got that particular symptom.
If you're working in a .Net app you might want to consider 'upgrading' it to Pos .Net... it has some extra features and is still compatible with the OPOS legacy drivers. Also, if you can find native .Net drivers then you can avoid COM in your solution altogether.
Good luck. Yort,
It's been a while but I'm back looking at printer problems again.
You said in your last message I should consider upgrading to Pos .Net. I though that was what was already using (Microsoft.PointOfService). Is this legacy and, if so, which import should I be using?Thanks
Mike
- Hi Mike,
I think I got confused in your previous post when you said you were new to OPOS, OPOS is a legacy COM based implementation of the UPOS spec where as Pos .Net (which IS what you are using) is the .Net implementation. When you said OPOS I thought you actually meant OPOS instead of Pos .Net... but it sounds like I just assumed too much ;)
You might want to make sure you are using (or at least try) native Pos .Net drivers with Pos .Net instead of the legacy OPOS ones, where native ones are available. In theory the Pos .Net ones should be better, and they shouldn't have deployment issues (dll ____, com registration etc)... although I've had some instances where both types were available for a specific device and I prefered the behaviour of the old OPOS drivers which was upsetting. Pos .Net still works with the OPOS service objects which is good for compatibility, but like I said, native Pos .Net service objects *should* be better. Epson provide both, and are one of the few manufacturers with native Pos .Net drivers.
You could also check you are using the latest version of Pos .Net, 1.12. I hear a rumout that a 1.13 release is being prepared too, but I don't think it's available yet.
Good luck. - Hi Mike,
Just stumbled over this, while investigating a different Epson / OPOS issue, and may have something to offer on the AsyncMode = false issue.
I was having the same kind of problem re attempting to claim the printer with AsyncMode = false would lock the GUI for several seconds, even though I was printing from a background thread.
I'm using the CCOs from monroecs.com, and found the following in their "readme":
" * Event firing logic supports well-behaved service objects that
fire events from the thread that created the control , plus other
service objects that fire them from other threads."
I had been instantiating the CCOs from the main thread at startup, then running actual print jobs from background threads and getting the GUI lockup.
I switched to instantiating the CCOs from a background thread in addition to printing from a background thread and all the GUI lockups have gone, even with AsyncMode = false.
As for different printers behaving differently, at one point I had a Sam4s Ellix 20, that would respond with a full set of success codes (& no hang) even if it was still in the box! (I was eventually supplied with updated drivers that fixed the problem)
Cheers
Andy
- Andy
Woah! You may well become the love of my life ;-)
This matches exactly the problem I'm having. I'll get the printer back and have a play tomorrow and post back.
Cheers
Mike - Andy,
Okay, I'm ready to have your babies - I just need to find a womb, some maternal instinct and a blindfold.
A bit more background and info for anyone. I could be wrong about anything I say so don't hold me to it, but it's what I've found.
Our software is being deployed higher end restaurants. In this particular case they had 6 dot-matrix printers and 4 thermals. When an order is sent only specific products from the order as a whole is printed to each printer (e.g. desserts only went to 2 printers, mains went to 5, drinks went somewhere else etc).
From testing (and the documentation implies the same thing though I'm terrible at reading documenation a lot of the time), I think that .Claim only works locally, so if you have 5 tills all trying to print to the same printer, forget .Claim - it doesn't work in the sense that it doesn't ensure that one till has access to it and the rest don't. It only guarantees that only one process on the local machine has access.
The solution wasn't the easiest (basically I assigned one till as a print server and other tills sent prints over TCP for the print server to queue and print) and with a re-directed printer setting (in case one print failed it could re-direct to another printer) this now seems resiliant.
Each print job I created as a separate thread (created by the printing thread). All the CCOs were created in the print thread - you get duplicated OCX creation errors if you try to create them in the individual print thread - seems the event handler is single instance.
I stopped using the Epson Pos .Net drivers because it regularly showed up 'Initializing' or 'the printer is not Initialized' errors that I didn't get from the OCX versions.
Dull and imprecise I know, and by that I mean OPOS and Pos .Net, not me, though I know what you're thinking.
Cheers
Mike


