.NET Framework Developer Center > .NET Development Forums > POS for .NET > OPOS Printing hanging main application although printing done in secondary thread
Ask a questionAsk a question
 

AnswerOPOS Printing hanging main application although printing done in secondary thread

  • Saturday, August 15, 2009 3:27 PMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Sunday, August 16, 2009 7:49 PMYortAnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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

  • Saturday, August 15, 2009 4:12 PMSean LimingMVP, AnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Saturday, August 15, 2009 5:24 PMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Saturday, August 15, 2009 9:18 PMYortAnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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 ?

  • Saturday, August 15, 2009 9:59 PMSean LimingMVP, AnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Saturday, August 15, 2009 10:02 PMYortAnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Sunday, August 16, 2009 2:03 PMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.Net

    Public 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 Structure

        Public Enum OrderPrintJobType
            Kitchen = 0
            Waiter = 1
            Receipt = 2
        End Enum

        Public 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 Boolean

        Public Shared Sub StartPump()
            PumpThread.IsBackground = True
            PumpThread.Name = "Printing Thread"
            PumpThread.Priority = Threading.ThreadPriority.Lowest
            PumpThread.Start()
        End Sub

        Private Shared Sub OrderPrintJobPump()

            While True
                While OrderPrintJobs.Count > 0
                    Dim kpj = OrderPrintJobs.Dequeue

                    If Not kpj.done Then PrintToOrderPrinterEx(kpj)
                End While

                Threading.Thread.Sleep(5000)
            End While

        End 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 Sub

        Private 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 Boolean

            For Each printer In Printers
                If PrinterName.ToUpper = PrintersLDN.Item(LDNIndex).ToUpper Then
                    Dim PrintError As Boolean = True
                    IsReprint = False
                    PrinterErrorEvent = False

                    Do Until (PrintError = False)
                            With printer
                                Try
                                    .Claim(1000)

                                    If .Claimed Then
                                        AddHandler .ErrorEvent, AddressOf DeviceErrorEventHandler
                                        AddHandler .OutputCompleteEvent, AddressOf OutputCompleteEventHandler

                                        IsReprint = PrinterErrorEvent

                                        PrinterIdleEvent = False
                                        PrinterErrorEvent = False

                                        Try
                                            .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()
                                            Next

                                            PrintError = 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 If

                                            End 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 If

                                Catch 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 Try

                                Try
                                    .DeviceEnabled = False
                                    .Release()
                                Catch ex As Exception
                                End Try

                            End With
                    Loop
                End If

                LDNIndex += 1
            Next
        End Sub

        Private Shared Sub DeviceErrorEventHandler(ByVal Sender As Object, ByVal e As DeviceErrorEventArgs)
            PrinterErrorEvent = True
            e.ErrorResponse = ErrorResponse.Clear
        End Sub

        Private Shared Sub OutputCompleteEventHandler(ByVal Sender As Object, ByVal e As OutputCompleteEventArgs)
            PrinterIdleEvent = True
        End Sub

    End Class

    Cheers

    Mike

     

  • Sunday, August 16, 2009 7:49 PMYortAnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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
    •  
  • Sunday, August 16, 2009 8:20 PMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Sunday, August 16, 2009 10:54 PMYortAnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Saturday, October 17, 2009 11:16 AMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

     

  • Saturday, October 17, 2009 7:11 PMYortAnswererUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Thursday, October 22, 2009 11:15 AMAndy.S Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

  • Tuesday, October 27, 2009 4:56 PMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Saturday, October 31, 2009 7:33 PMMike Hoffman Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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