Control a called routine through RunProgram()

Just starting out? Need help? Post your questions and find answers here.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Thu Oct 20, 2022 7:39 pm No, I mean you don't need to handle: #PB_NetworkEvent_Connect #PB_NetworkEvent_Disconnect
Thanks infratec, I since read about it being "connectionless", as it says in the docs. Actually, the way I need to do this is for the "calling" routine to send data to the "called" routine, and for the "called" routine to send data back. Do I need to open a UDP server at both ends with different port numbers? It's already working as a "called" prog receiving data and proving it by putting it into a file (both below). It's a background process that doesn't need a console display.

Code: Select all

; ** Mainprog.pb - Main calling routine
OpenConsole()
If RunProgram("d:\udpprog\subprog.exe", "", "", #PB_Program_Open )
  ConnectionID = OpenNetworkConnection("127.0.0.1", 24 , #PB_Network_UDP, 5000 ) 
  Repeat      
    Print("Please enter a string to send to the background process 'subprog.pb' or q to quit : ")
    str$ = Input()
    SendNetworkString(ConnectionID, str$, #PB_Unicode); 
      
    If str$="q"
      End
    EndIf
  ForEver
EndIf

Code: Select all

; ** d:\udpprog\Subprog.pb - Sub prog routine
*Buffer = AllocateMemory(1000)
If CreateNetworkServer(0, 24, #PB_Network_IPv4 | #PB_Network_UDP, "127.0.0.1")
  Repeat      
    ServerEvent = NetworkServerEvent()
    If ServerEvent
      ClientID = EventClient()
      Select ServerEvent
        Case #PB_NetworkEvent_Data
          result.i = ReceiveNetworkData(ClientID, *Buffer, 1000)
          strinp$=Left(PeekS(*Buffer, -1, #PB_Unicode),result.i)
          output.s + strinp$
          
          If Left(strinp$,1) = "q"
            CreateFile(0,"d:\udpprog\output.txt")
            WriteString(0,output.s)
            CloseFile(0)
            quit = 1
          EndIf
      EndSelect
    EndIf
  Until Quit = 1
EndIf
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: Control a called routine through RunProgram()

Post by Olli »

Oso wrote:Apologies also for the apparent double-post
Absolutely no problem ! 8)

I know this light bug which sometimes occurs.

And I think, when I will switch my computer on, I will test and give back the example of infratec. I shut several network tasks down since I bought it, but W10 seems to be the king (vs older versions) of the local network pipes.

Also, infratec mentionned a freeed CPU, so I have an old word in the head : DMA. And Fred removed the several identifying numbers of patents I published on the french forum by his removing of the french off topic later, as it was not a work to be shared (names of the 3 scientists, dates of studying, dates of publishing, associated societies). I published it, 5 years ago, when my dad was paralysed. It was 3 patents about network, DMA, and harddrive :
1) harddrive/network DMA channel
2) network/external server DMA channel
3) distant DMA control

CPU absolutely freeed ! :shock:

So, I imagine if I wanted to start a debate against infratec, considering the classical process pipes would be faster than network i/o : I would be directly dead !!

Network is as fast as video now, so it is a very interesting subject.
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

Code: Select all

; ** d:\udpprog\Subprog.pb - Sub prog routine

If CountProgramParameters() = 1
  
  Port = Val(ProgramParameter(0))
  
  *Buffer = AllocateMemory(1000)
  If CreateNetworkServer(0, Port, #PB_Network_IPv4 | #PB_Network_UDP, "127.0.0.1")
    ConnectionID = OpenNetworkConnection("127.0.0.1", Port + 1, #PB_Network_UDP, 1000)
    Repeat
      If NetworkServerEvent() = #PB_NetworkEvent_Data
        result.i = ReceiveNetworkData(EventClient(), *Buffer, 1000)
        If result
          strinp$ = PeekS(*Buffer, result, #PB_UTF8|#PB_ByteLength)
          SendNetworkString(ConnectionID, "Ok")
          output.s + strinp$ + #CRLF$
          Debug "Received: " + strinp$
          If Left(strinp$,1) = "q"
            CreateFile(0, "output.txt")
            WriteString(0, output.s)
            CloseFile(0)
            quit = 1
          EndIf
        EndIf
      Else
        Delay(10)
      EndIf
    Until Quit = 1
    CloseNetworkServer(0)
  EndIf
  
EndIf

Code: Select all

; ** Mainprog.pb - Main calling routine
OpenConsole()
Prog = RunProgram("subprog.exe", "5000", "", #PB_Program_Open)
If Prog
  ConnectionID = OpenNetworkConnection("127.0.0.1", 5000 , #PB_Network_UDP)
  *Buffer = AllocateMemory(2048)
  Server = CreateNetworkServer(#PB_Any, 5001, #PB_Network_UDP, "127.0.0.1")
  Print("Please enter a string to send to the background process 'subprog.pb' or q to quit : ")
  Repeat
    Key$ = Inkey()
    If Key$ = #CR$
      Debug "Send: " + str$
      SendNetworkString(ConnectionID, str$)
      If str$ = "q"
        ExitSoon = #True
      EndIf
      str$ = ""
    Else
      str$ + Key$
    EndIf
    
    If NetworkServerEvent(Server) = #PB_NetworkEvent_Data
      Length = ReceiveNetworkData(EventClient(), *Buffer, MemorySize(*Buffer))
      If Length
        Received$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength)
        Debug "Received: " + Received$
        If ExitSoon
          Exit = #True
        EndIf
      EndIf
    EndIf
    
  Until Exit
  FreeMemory(*Buffer)
  CloseNetworkServer(Server)
  CloseNetworkConnection(ConnectionID)
  CloseProgram(Prog)
EndIf
CloseConsole()
Without threads, the main loop needs to run continiously.
So It was necesarry to replace Input() by Inkey().

Due to less time, I did not use EnableExplizit.
You should definately use it always in every program.
It saves you time. Trust me.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Thu Oct 20, 2022 10:06 pm Without threads, the main loop needs to run continiously. So It was necesarry to replace Input() by Inkey().
Many thanks indeed Infratec, I appreciate all you've done for me here. Yes, I fully understand why you included the thread - without the thread for listening for received data, we have to deal with local keyboad input and received data inside the same loop. To be honest, I agree that threading is the answer, but I haven't become proficient with it yet. I don't really follow the syntax well - it seems a bit arcane to me, but I'm sure I'll get there in the end :D
infratec wrote: Thu Oct 20, 2022 10:06 pm Due to less time, I did not use EnableExplizit. You should definately use it always in every program. It saves you time. Trust me.
Yes, agreed, I know exactly what you mean. I use EnableExplicit in programmes that I intend to keep and put into production, but not when testing. I prefer to keep test code as short as possible.

I've been testing the main prog and sub prog and you've implemented it as I thought would be necessary - i.e. an IP port for the main prog and a port for the sub-prog. I've also just extended it to call two sub-progs at the same time and that works too, although I think I need to refine the below slightly. I don't think I'm necessarily receiving the data back from the specific server, because EventClient() isn't being specific, but I'll work on this further.

Code: Select all

    If NetworkServerEvent(Server1) = #PB_NetworkEvent_Data
      PrintN("Received something from subprog 1")
      Length = ReceiveNetworkData(EventClient(), *Buffer, MemorySize(*Buffer))
      If Length
        Received$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength)
        PrintN("Received back from subprog 1 : " + Received$)
User avatar
mk-soft
Always Here
Always Here
Posts: 6209
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by mk-soft »

Before working with networking, one should learn the basics:
- ISO model, Protocols (IP, UDT, TCP, etc). Link: https://en.wikipedia.org/wiki/OSI_model
- SendNetworkData passes the data to the send buffer. Not that they have been sent
- ReceiveNetworkData fetches the receive buffer. Not separated according to sent data, but the data accumulated up to that point.
- There are separate send and receive buffers for each connection. Distinguishable with EventClient(), EventServer()
- It is your task to ensure that the data is complete. For example, with a header or with start and end characters.
- With TCP it is guaranteed that the data up to 64kB are in the correct order in the receive buffer. Not the completeness.

Example with UDP as short text sending ..

Link: viewtopic.php?t=74200
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Thu Oct 20, 2022 10:06 pm

Code: Select all

; ** d:\udpprog\Subprog.pb - Sub prog routine
[...]
ConnectionID = OpenNetworkConnection("127.0.0.1", Port + 1, #PB_Network_UDP, 1000)
A quick question Infratec... this threw me a bit at first. I noticed that when I tried to change the case of "ConnectionID" to "connectionid", PB changed it back again. This lead me to find out that ConnectionID is actually a PB function. In fact I thought at first that it was a function, because of the capitalisation :D I see now that you prefer capitalised variable names.

Just out of interest, I put a debug in my code and ConnectionID(connectionid) returned 188 in both concurrent instances of the running code. Just curious what the PB ConnectionID() function is for? The help doesn't explain much. It just says "Returns the system identifier".
Last edited by Oso on Fri Oct 21, 2022 6:02 pm, edited 1 time in total.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

mk-soft wrote: Fri Oct 21, 2022 11:28 am Before working with networking, one should learn the basics:
Thanks for the information mk-soft :)
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

Olli wrote: Thu Oct 20, 2022 9:06 pm I published it, 5 years ago, when my dad was paralysed. It was 3 patents about network, DMA, and harddrive
Sorry to hear about your dad, @Olli. I hope all was well.
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

You still can use ConnectionId as variable. It comes not to a conflict.

Normally the PB procedures returns a PB internal value. But sometimes you need to use API functions to reach your target.
And the API functions needs the system value and not the PB internal value.
For these cases therte are some PB procedures which returns you the system internal value.

In the case of a network connection it will return the socket handle of the underlaying os function.
With this socket handle you can use setsockopt_() for example.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Fri Oct 21, 2022 7:28 pm You still can use ConnectionId as variable. It comes not to a conflict. Normally the PB procedures returns a PB internal value. But sometimes you need to use API functions to reach your target. And the API functions needs the system value and not the PB internal value.
Thanks infratec I've got it. I hadn't realised variable naming was quite as flexible as that. I see what you mean about the system value.

Just to wrap this up and for anyone who may be interested, the code below provides a speed test of multiple processes. It only requires a single port 6000 for the main process server, but the sub-processes are numbered from 5000. I've tested it with 200 sub-processes. It keeps the programme ids. and the connection ids. in two arrays, prog.i() and connectid.i().

To use it run Mainprog.pb. Tell it how many processes to open and then you need to navigate back to the main process window. As soon as you press a key in the main window (not the sub-process windows), it will replicate that key out to all the other processes and they will reply back to the main process with a repeat of it. To close the sub-processes, press q. I know it's a bit of a weird test, but it shows the performance. Bear in mind the code is intended to be stored in d:\infratec_server_async\.

Code: Select all

; ** d:\infratec_server_async\Subprog.pb - Sub prog routine
OpenConsole()
*Buffer = AllocateMemory(1000)

Port = Val(ProgramParameter(0))         ; * Port is first parameter passed to this routine
id.s = ProgramParameter(1)              ; * Id. enables this routine to pass back its ID string to be identified from others

PrintN("Subprog starting - ID : " + id.s + " - I'm listening as a server on port : " + port)
If CreateNetworkServer(0, Port, #PB_Network_IPv4 | #PB_Network_UDP, "127.0.0.1")
  conid = OpenNetworkConnection("127.0.0.1", 6000, #PB_Network_UDP, 1000)       ; * Connect back to calling routine's server
  Repeat
    If NetworkServerEvent() = #PB_NetworkEvent_Data                             ; * Test if this server has received data
      result.i = ReceiveNetworkData(EventClient(), *Buffer, 1000)               ; * Receive buffer contents from calling prc
      If result
        strinp$ = PeekS(*Buffer, result, #PB_UTF8|#PB_ByteLength)               ; * Obtain the buffer contents
        SendNetworkString(conid, ID.S + " Returning : " + strinp$)              ; * Send confirmation back to calling proc.

        output.s + strinp$ + #CRLF$                                             ; * Store the buffer so we can save to log
        PrintN(strinp$)                                                         ; * Display the string received
        If Left(strinp$,1) = "q" Or Left(strinp$,1) = "Q"                       ; * If told by server to quit (q/Q)
          quit = 1                                                              ; * Close this routine completely
        EndIf
      EndIf
    Else
      Delay(10)
    EndIf
  Until Quit = 1
  CloseNetworkServer(0)                                                         ; * Close this routine's server
EndIf

Code: Select all

; ** d:\infratec_server_async\Mainprog.pb - Main calling routine
OpenConsole()
Dim prog.i(500)
Dim connectid.i(500)

*Buffer = AllocateMemory(2048)

Print("How many sub-processes do you wish to start? : ")
pcount.i = Val(Input())

For pno.i = 1 To pcount.i     ; * Count through requested number of sub-processes and run each
  prog(pno.i) = RunProgram("d:\infratec_server_async\subprog.exe", Str(pno.i + 4999) + " SUBPROG" + Str(pno.i), "", #PB_Program_Open)
  connectid.i(pno.i) = OpenNetworkConnection("127.0.0.1", pno.i + 4999 , #PB_Network_UDP)   ; * Open to sub-process server
  PrintN("Connection id. : " + Str(connectid.i(pno.i)))                                     ; * Connection id. to sub-proc
Next pno.i

server.i = CreateNetworkServer(#PB_Any, 6000, #PB_Network_UDP, "127.0.0.1")                 ; * Open this server side x 1
PrintN("Please proceed to type text to send to the background processes in real time (press q to quit : ")

Repeat
  Key$ = Inkey()                                                                            ; * Input key at a time
  For pno.i = 1 To pcount.i                                                                 ; * Go through each sub-proc.
    SendNetworkString(connectid(pno.i), key$)                                               ; * Send to each sub-process
  Next pno.i                                                                                ; * ... server
  If key$ = "q" Or key$ = "Q"                                                               ; * This routine handles the
    Exit = #True                                                                            ; * quit function, sent just
  EndIf                                                                                     ; * same as any other key
    
  If NetworkServerEvent(server.i) = #PB_NetworkEvent_Data                                   ; * Check received this server
    cl.i = EventClient()                                                                    ; * Client id. (this side only)
    Length = ReceiveNetworkData(cl.i, *Buffer, MemorySize(*Buffer))                         ; * No. of bytes in buffer
    If Length                                                                               ; * If something received
      Received$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength)                           ; * Obtain the buffer contents
      PrintN(Str(cl.i) + " " + Received$)
    EndIf
  EndIf
Until Exit

FreeMemory(*Buffer)
CloseNetworkServer(server.i)                                                                ; * Close this server

For pno.i = 1 To pcount.i                                                                   ; * Go through each sub-process
  CloseNetworkConnection(connectid(pno.i))                                                  ; * Close connection to sub-proc
  CloseProgram(prog(pno.i))                                                                 ; * Close programme space (but
Next pno.i                                                                                  ; * not killing it).
CloseConsole()
Post Reply