can a tread access a normal Function

General FreeBASIC programming questions.
Gablea
Posts: 1049
Joined: Apr 06, 2010 0:05
Location: Northampton, United Kingdom
Contact:

can a tread access a normal Function

Postby Gablea » Sep 01, 2018 21:29

Hi all,

This may Be a daft question but I was hoping someone could advice me on this

I have been thinking about dropping my DOS Support (And A LOT of you will be screaming "about time") and I was thinking about moving onto Linux (Debian or FreeBSD) or windows.

I have been looking at the idea of moving my barcode Reader support into a thread on its own (so it will run no matter what the program is doing)

Code: Select all

Sub PriceCheck_SignedOff
      ScannerFucntion = "SignedOff_PriceCheck"
   MenuBeingDisplayed = "PriceCheckSignedOff"

   ScreenLock
      PriceCheckDisplay
      DisplaySalescreenMenu(MenuBeingDisplayed)
      DisplayFunctionKeyLabels
      DisplayTaskBar(1)
   ScreenUnLock
   
   Select Case ScannerType
      Case "Com", "COM", "com"
         Do : DisplayTaskBar(1) : ProcessKey_PriceCheckSignedoff(GetKeyNB) : SelectSerialScanner
         Loop
      
      Case"KBW", "kbw"," Kbw", ""
         Do : DisplayTaskBar(1) : ProcessKey_PriceCheckSignedoff(GetKeyNB)
         Loop
   End Select
End Sub


that is my current code to display the price check screen. but at the moment i am having a small issues with keeping the date & time showing correctly. (when running on a windows test machine the clock on the Windows taskbar could say 15:22 but the Time on the PoS program could say 15:19 (and not change unless i restart the software)

so what i was thinking was to move the selectSerialScanner into a tread on its own

Code: Select all

Private Sub SelectSerialScanner
   Do While LOC(BarcodeReader) > 0
      Buffer = ""
        
      buffer = Input(1,BarcodeReader)
              
      If buffer <> Chr(Val(ScannerPrefix)) Then
         dim a as string = buffer
            dim result as string
               For x as integer = 0 to len(a) -1
                  If instr(chr(a[x]),any "0123456789" & Chr(Val(ScannerPrefix))) then
                     result += chr(a[x])
                  End If
               Next x
            datareceived += result
      End If
   
      'If DebugMode = 1 Then
          Open Cons For Output As #DebugConsole
            Print #DebugConsole, "Data From Scanner"
            Print #DebugConsole, datareceived
         Close #DebugConsole
      'End If
   
      If buffer = Chr(Val(ScannerPrefix)) Then      'tells NPoS the scanner is done
         If RemoveNumber > 0 then
            Dim Temp as string = mid$(datareceived, RemoveNumber, len(datareceived))
               datareceived = ""
               datareceived = Temp
         End If
         ScannerFucntionSub(datareceived)
         datareceived = ""        
      End If
   Loop       
End Sub

this is the code that works the scanner (will have to add support for the Scale soon) as you can see when it gets the ScannserPrefix chr (normally the enter chr) the software would then use the ScannerFucntionSub to process the scanned barcode number


Code: Select all

Private Sub ScannerFucntionSub(ByVal BarcodeNumber As String)
   datareceived = ""
          Buffer = ""
          
   Select Case ScannerFucntion
      Case "SaleScreen"
         SubTotalPressed = 0
         FindProduct(BarcodeNumber)
               
      Case "Salescreen_PriceCheck"
         FindProduct_PriceCheck(BarcodeNumber, "SaleScreen")
            
      Case "SignedOff_PriceCheck"
         FindProduct_PriceCheck(BarcodeNumber, "SignedOff_PriceCheck")
            
      Case "GiftCard"

   End Select
end Sub


The FindProdoct code is independ to the screen etc so would I have to make it accessible to the thread or does the ScannerFucntionSub make the program drop out of the tread and run that code while still running the SelectSerialScanner in a thread?

or have i totally got the wrong end of how threads work.
badidea
Posts: 2150
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: can a tread access a normal Function

Postby badidea » Sep 01, 2018 22:49

I haven't studied your code in detail, nor am I an expect on threads. But if one thread is still creating the barcode, while the other thread it already processing it, this is a problem.

If the scanning is a blocking operation (waiting for several seconds until ready or forever) and you want other things, like update the time, to continue, then the only solution is threads I think.

But I wonder if the scanner is (or needs to be) blocking. Maybe it is possible to poll the scanner every time.
Is there new data? Yes, Read it and go back. No? Go back.
If this is possible, and I think it is because it looks like checking if a file size has increased and the additional bytes are read.
The disadvantage is that your barcode will arrive in bits and pieces. So you need to keep track of these bit and pieces. Concatenate them and detect when complete or when a new prefix or barcode starts.
badidea
Posts: 2150
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: can a tread access a normal Function

Postby badidea » Sep 01, 2018 23:41

To illustrate blocking vs non-blocking, the following two similar codes:
Press normal character key and finish a word with a " " (space-bar key).
"quit" + space-bar will end both programs (not "quit" + enter).
Notice the time display in both cases.

Blocking:

Code: Select all

function readWord() as string
   var key = "", word = ""
   do
      key = inkey
      while key = ""
         key = inkey
         sleep 1,1
      wend
      if key = " " then exit do 'space ends input
      print key;
      word &= key
   loop
   return word
end function

dim as integer cursorRow, cursorPos
var text = ""
do
   cursorRow = CsrLin
   cursorPos = Pos
   locate 10, 30: print time
   locate cursorRow, cursorPos

   print "Input: ";
   text = readWord()
   print
   print "text = " & text
loop until text = "quit"

Non-blocking:

Code: Select all

dim as integer cursorRow, cursorPos
var key = "", text = "", word = ""
do
   cursorRow = CsrLin
   cursorPos = Pos
   locate 10, 30: print time
   locate cursorRow, cursorPos

   key = inkey
   print key;
   if key = " " then 'space
      word = text
      print
      print "text = " & word
      text = ""
   else
      text &= key
   end if
   sleep 1,1
loop until word = "quit"
Gablea
Posts: 1049
Joined: Apr 06, 2010 0:05
Location: Northampton, United Kingdom
Contact:

Re: can a tread access a normal Function

Postby Gablea » Sep 02, 2018 18:21

so if I moved the serial barcode function to a thread it would be independent to the main loop.

the reason why ive been thinking about this is to speed up the scanning function.

the scanner output for example


F1234567890123 (chr13)

so my loop only works with numbers.

I assume the thread can send data to a non threaded function? i.e. ScannerFucntionSub("1234567890123") (and would that still work as normal)

if I have understood this right it would just be the function that get the barcode data from the scanner in the first place (I would also assume once I get the status of the printer working I can have that in a thread on its own)
MrSwiss
Posts: 3633
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: can a tread access a normal Function

Postby MrSwiss » Sep 02, 2018 18:44

A Thread works by default with a Sub (Function isn't possible), defined as follows:

Declare As Sub thr_run( Byval As Any Ptr ) ' as described in FB-Doc's.
If you want Input/Output from the Thread, you'll need to define a Type, that holds/returns
the needed information. IMPORTANT: use a Mutex (in thr_run() as well, as in Main-Loop).

Simple Time Thread implementation (above explained in code):

Code: Select all

' Time_Thread_1.bas -- 2018-08-14, MrSwiss
'
' compile: -s console
'

' thread data-type (typedef struct in C-language)
Type thr_data                           ' a default initialized data-type
    As ZString * 9  sTime = ""          ' actual payload (user data, the time)
    As Boolean      cont  = TRUE        ' thread procedure control (loop run/quit)
End Type

Dim As thr_data     dt1                 ' we need one of above types for this thread
Dim Shared As Any Ptr   MLock           ' define the common MUTEX (to all threads!)

' the threads 'worker' procedure
Sub thr_run( ByVal pd As Any Ptr )      ' ptr 'pd', provides access to above type
    Dim As thr_data Ptr ptd = pd        ' convert 'any ptr' to 'data-type ptr' !!!
    While ptd->cont                     ' run, until Boolean turns FALSE (thread quit)
        MutexLock(MLock)                ' lock mutex (for as long as we process data)
        If ptd->sTime <> Time Then
            ptd->sTime = Time           ' update time, when changed only!
        End If
        MutexUnLock(MLock)              ' free mutex (any other thread can use it now)
        Sleep(200, 1)                   ' take a break (length defines time accuracy!)
    Wend                                ' 200 mS is sufficiently accurate and saves _
End Sub                                 ' a lot of resources, for other processes!

' get our thread up and running
MLock = MutexCreate()                   ' create common MUTEX (mutually exclusive)
Dim As Any Ptr  thread = ThreadCreate(@thr_run, @dt1)   ' create thread (one shot)
If thread = 0 Then                      ' error check (no error = no action)
    Print "thread creation failed! ";   ' on error, inform user, then wait
    Beep : Sleep : End 1                ' on user action --> error-quit
End If
' thread started successfully

' ===== start main =====
Dim As String   hi = "Welcome, to FreeBASIC's threading ... "

Width 80, 25                            ' define standard console size
Locate  1, 60 : Print "thread -->"      ' since we don't use cls, once only needed
Locate 21,  1 : Print "press a key, to EXIT!"

Do
    MutexLock(MLock)                    ' MAIN run's exclusive (other thread stopped)
    Color 12                            ' hilight thread output (bright red)
    Locate  1, 72 : Print dt1.sTime;    ' output whatever the thread deliveres
    Color 7                             ' reset color to default
    Locate 11,  2 : Print "MAIN --> " + hi; ' this is just for control purposes
    ' do any other mighty important stuff from here ...

    ' finish mighty important before this line
    MutexUnLock(MLock)                  ' release MAIN lock
    Sleep(500, 1)
Loop Until Len(InKey()) > 0             ' on user action --> exit program
' ===== end main =====
' clean up ...  in oposite order of creation!
dt1.cont = FALSE                        ' stop thread procedure
ThreadWait(thread)                      ' wait for thread stop
MutexDestroy(MLock)                     ' delete mutex
Gablea
Posts: 1049
Joined: Apr 06, 2010 0:05
Location: Northampton, United Kingdom
Contact:

Re: can a tread access a normal Function

Postby Gablea » Sep 02, 2018 18:54

Thank you for that MrSwiss

So for this to work I would need to have it as sub (like i have)

Code: Select all

Private Sub SelectSerialScanner
   Do While LOC(BarcodeReader) > 0
      Buffer = ""
        
      buffer = Input(1,BarcodeReader)
              
      If buffer <> Chr(Val(ScannerPrefix)) Then
         dim a as string = buffer
            dim result as string
               For x as integer = 0 to len(a) -1
                  If instr(chr(a[x]),any "0123456789" & Chr(Val(ScannerPrefix))) then
                     result += chr(a[x])
                  End If
               Next x
            datareceived += result
      End If
   
      'If DebugMode = 1 Then
          Open Cons For Output As #DebugConsole
            Print #DebugConsole, "Data From Scanner"
            Print #DebugConsole, datareceived
         Close #DebugConsole
      'End If
   
      If buffer = Chr(Val(ScannerPrefix)) Then      'tells NPOS the scanner is done
         If RemoveNumber > 0 then
            Dim Temp as string = mid$(datareceived, RemoveNumber, len(datareceived))
               datareceived = ""
               datareceived = Temp
         End If
         ScannerFucntionSub(datareceived)
         datareceived = ""        
      End If
   Loop       
End Sub


so at the start of the program I would use

MLock = MutexCreate() ' create common MUTEX (mutually exclusive)
Dim As Any Ptr thread = ThreadCreate(@SelectSerialScanner, @dt1) ' create thread (one shot)
If thread = 0 Then ' error check (no error = no action)
Print "thread creation failed! "; ' on error, inform user, then wait
Beep : Sleep : End 1 ' on user action --> error-quit
End If

Am I on the right track?
but the tread does not output any data to the screen the data would be used inside the program (the input comes from the serial port)
MrSwiss
Posts: 3633
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: can a tread access a normal Function

Postby MrSwiss » Sep 02, 2018 19:03

I'm not sure you've understood the principle of a Thread.

It just fills the used struct (type thr_data) with the current Time (String, Len=8).
(in your case: the barcode-string), if and when, if ever, you are going to use it,
is entirely up to your code (in Main, or another procedure).

I'd advocate, to take your time, to study the provided example, in detail ...

Gablea wrote:but the tread does not output any data to the screen the data would be used inside the program (the input comes from the serial port)
Yes, correct! (where the data comes from, is immaterial in the thread context)
It could be: File / Network or anything else, available from existing resources ...

Return to “General”

Who is online

Users browsing this forum: No registered users and 12 guests