Jump to content

Array-Error


Chinx7
 Share

Recommended Posts

Hello,

I'm lurking on this forum for quite some time and it helped me a lot. But this time I couldn't find anything for my problem and neither could I on google.

I am using autoit for a couple of years, but nothing serious, just some casual automation, so I would still consider myself a beginner.

This time I tried to do a bigger "project" and butchered together some things i found on here and wrote some myself. 

My program consists of 2 scripts, a server and a client. The Server just handles the server stuff (duh) and some small calculations. 

The Client connects to a media player (currently only vlc) and communicates with it (only play/pause and current time of the video).

It's nothing fancy, GUI and comments are still all over the place and more of a proof of concept.

But now I hit a wall and I can't figure it out myself.

When I connect the client to the server and try to send some data from the media player I get the error:

""C:\Users\chinx\Documents\Autoit\VideoSyncer\start vlc with webinterface.au3" (78) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
Global $GlobalPlaystate = $playandtimeA[1]
Global $GlobalPlaystate = ^ ERROR" 

On the Server I get the same in line 88 "$Timestamp = $PlayPauseAndTimestamp[1]"

I tried to increase the Array size before using it.

I get a console output with:

"0
:0"

and expected is "0:0"

So I thought it could be a white space or a @CRLF causing the problem so I stripped it with StringStripWS. But I still get the same output.

I tested some other things I can't remember and I just don't know how to proceed anymore, so I'm asking for help if anyone knows what the problem is.

 

I hope the formatting is correct and I hope I don't have anything personal in the scripts, but here they are:

Server:

#cs ----------------------------------------------------------------------------

AutoIt Version: 3.3.8.1 (Or greater)
Author:      Ken Piper

Script Function:
    Template multi-client server base code.
    Use as a base for making an efficient server program.

    This base will just accept connections and echo back what it receives,
        and kill the connection if it is dead or inactive for x seconds.
    It will not do any other work, that must be added seperately!

#ce ----------------------------------------------------------------------------
#include <Array.au3>


TCPStartup()
Opt("TCPTimeout", 0)

#region ;Safe-to-edit things are below
Global $BindIP = "0.0.0.0"    ;Listen on all addresses
Global $BindPort = 8080        ;Listen on port 8080
Global $Timeout = 15000        ;Max idle time is 15 seconds before calling a connection "dead"
Global $PacketSize = 2048    ;Max packet size per-check is 2KB
Global $MaxClients = 50        ;Max simultaneous clients is 50
Global $RangeTimestamp = 1000   ;Allowed Range of Timestamp +/-
#endregion ;Stuff you shouldn't touch is below

Global $Listen
Global $Clients[1][6] ;[Index][Socket, IP, Timestamp, Buffer]
Global $Ws2_32 = DllOpen("Ws2_32.dll") ;Open Ws2_32.dll, it might get used a lot
Global $NTDLL = DllOpen("ntdll.dll") ;Open ntdll.dll, it WILL get used a lot
Global $CleanupTimer = TimerInit() ;This is used to time when things should be cleaned up

OnAutoItExitRegister("Close") ;Register this function to be called if the server needs to exit

$Clients[0][0] = 0
$Listen = TCPListen($BindIP, $BindPort, $MaxClients) ;Start listening on the given IP/port
If @error Then Exit 1 ;Exit with return code 1 if something was already bound to that IP and port

While 1
    USleep(1000, $NTDLL) ;This is needed because TCPTimeout is disabled. Without this it will run one core at ~100%.
    ;The USleep function takes MICROseconds, not milliseconds, so 1000 = 1ms delay.
    ;When working with this granularity, you have to take in to account the time it takes to complete USleep().
    ;1000us (1ms) is about as fast as this should be set. If you need more performance, set this from 5000 to 1000,
    ;but doing so will make it consume a bit more CPU time to get that extra bit of performance.
    Check() ;Check recv buffers and do things
    If TimerDiff($CleanupTimer) > 1000 Then ;If it has been more than 1000ms since Cleanup() was last called, call it now
        $CleanupTimer = TimerInit() ;Reset $CleanupTimer, so it is ready to be called again
        Cleanup() ;Clean up the dead connections
    EndIf
    Local $iSock = TCPAccept($Listen) ;See if anything wants to connect
    If $iSock = -1 Then ContinueLoop ;If nothing wants to connect, restart at the top of the loop
    Local $iSize = UBound($Clients, 1) ;Something wants to connect, so get the number of people currently connected here
    If $iSize - 1 > $MaxClients And $MaxClients > 0 Then ;If $MaxClients is greater than 0 (meaning if there is a max connection limit) then check if that has been reached
        TCPCloseSocket($iSock) ;It has been reached, close the new connection and continue back at the top of the loop
        ContinueLoop
    EndIf
    ReDim $Clients[$iSize + 1][6] ;There is room for a new connection, allocate space for it here
    $Clients[0][0] = $iSize ;Update the number of connected clients
    $Clients[$iSize][0] = $iSock ;Set the socket ID of the connection
    $Clients[$iSize][1] = SocketToIP($iSock, $Ws2_32) ;Set the IP Address the connection is from
    $Clients[$iSize][2] = TimerInit() ;Set the timestamp for the last known activity timer
    $Clients[$iSize][3] = "" ;Blank the recv buffer
    $Clients[$iSize][4] = "" ;Blank Play/Pause
    $Clients[$iSize][5] = "" ;Blank Timestamp
WEnd

Func Check() ;Function for processing
    If $Clients[0][0] < 1 Then Return ;If there are no clients connected, stop the function right now
    For $i = 1 To $Clients[0][0] ;Loop through all connected clients
        $sRecv = TCPRecv($Clients[$i][0], $PacketSize) ;Read $PacketSize bytes from the current client's buffer
        If $sRecv <> "" Then $Clients[$i][3] &= $sRecv ;If there was more data sent from the client, add it to the buffer
        If $Clients[$i][3] = "" Then ContinueLoop ;If the buffer is empty, stop right here and check more clients
        $Clients[$i][2] = TimerInit() ;If it got this far, there is data to be parsed, so update the activity timer
        #region ;Example packet processing stuff here. This is handling for a simple "echo" server with per-packet handling
            $sRecv = StringLeft($Clients[$i][3], StringInStr($Clients[$i][3], @CRLF, 0, -1)) ;Pull all data to the left of the last @CRLF in the buffer
            ;This does NOT pull the first complete packet, this pulls ALL complete packets, leaving only potentially incomplete packets in the buffer
            If $sRecv = "" Then ContinueLoop ;Check if there were any complete "packets"
            $Clients[$i][3] = StringTrimLeft($Clients[$i][3], StringLen($sRecv) + 1) ;remove what was just read from the client's buffer
            $sPacket = StringSplit($sRecv, @CRLF, 1) ;Split all complete packets up in to an array, so it is easy to work with them
            For $j = 1 To $sPacket[0] ;Loop through each complete packet; This is where any packet processing should be done
                $PlayPauseAndTimestamp = StringSplit ($sPacket[$j],":",2)
                $PlayPause = $PlayPauseAndTimestamp[0]
                $Timestamp = $PlayPauseAndTimestamp[1]
            Next
            ;Update Status of [4]&[5]
            $Clients[$i][4] = $PlayPause
            $Clients[$i][5] = $Timestamp
            ;Compare Data with other Clients
            for $k = 1 to $Clients[0][0]
                If $Clients[$i][4] <> $Clients[$k][4] Then
                    $discreptPP = 1
                EndIf
                If $Clients[$k][5] > $Clients[$i][5]+$RangeTimestamp AND $Clients[$k][5] < $Clients[$i][5]-$RangeTimestamp Then
                    $discreptTimestamp = 1
                EndIf
            Next
            If $discreptPP Or $discreptTimestamp Then
                For $l = 1 to $Clients[0][0]
                    TCPSend ($Clients[$l][0], $Clients[$i][4]&":"&$Clients[$i][5]&@CRLF)
                Next
            EndIf


            ;If needed send the updated data to Client
                ; How to Send Data: TCPSend($Clients[$i][0], "Echoing line: " & $sPacket[$j] & @CRLF) ;Echo back the packet the client sent
        #endregion ;Example
    Next
EndFunc

Func Cleanup() ;Clean up any disconnected clients to regain resources
    If $Clients[0][0] < 1 Then Return ;If no clients are connected then return
    Local $iNewSize = 0
    For $i = 1 To $Clients[0][0] ;Loop through all connected clients
        $Clients[$i][3] &= TCPRecv($Clients[$i][0], $PacketSize) ;Dump any data not-yet-seen in to their recv buffer
        If @error > 0 Or TimerDiff($Clients[$i][2]) > $Timeout Then ;Check to see if the connection has been inactive for a while or if there was an error
            TCPCloseSocket($Clients[$i][0]) ;If yes, close the connection
            $Clients[$i][0] = -1 ;Set the socket ID to an invalid socket
        Else
            $iNewSize += 1
        EndIf
    Next
    If $iNewSize < $Clients[0][0] Then ;If any dead connections were found, drop them from the client array and resize the array
        Local $iSize = UBound($Clients, 2) - 1
        Local $aTemp[$iNewSize + 1][$iSize + 1]
        Local $iCount = 1
        For $i = 1 To $Clients[0][0]
            If $Clients[$i][0] = -1 Then ContinueLoop
            For $j = 0 To $iSize
                $aTemp[$iCount][$j] = $Clients[$i][$j]
            Next
            $iCount += 1
        Next
        $aTemp[0][0] = $iNewSize
        $Clients = $aTemp
    EndIf
EndFunc

Func Close()
    DllClose($Ws2_32) ;Close the open handle to Ws2_32.dll
    DllClose($NTDLL) ;Close the open handle to ntdll.dll
    For $i = 1 To $Clients[0][0] ;Loop through the connected clients
        TCPCloseSocket($Clients[$i][0]) ;Force the client's connection closed
    Next
    TCPShutdown() ;Shut down networking stuff
EndFunc

Func SocketToIP($iSock, $hDLL = "Ws2_32.dll") ;A rewrite of that _SocketToIP function that has been floating around for ages
    Local $structName = DllStructCreate("short;ushort;uint;char[8]")
    Local $sRet = DllCall($hDLL, "int", "getpeername", "int", $iSock, "ptr", DllStructGetPtr($structName), "int*", DllStructGetSize($structName))
    If Not @error Then
        $sRet = DllCall($hDLL, "str", "inet_ntoa", "int", DllStructGetData($structName, 3))
        If Not @error Then Return $sRet[0]
    EndIf
    Return "0.0.0.0" ;Something went wrong, return an invalid IP
EndFunc

Func USleep($iUsec, $hDLL = "ntdll.dll") ;A rewrite of the _HighPrecisionSleep function made by monoceres (Thanks!)
    Local $hStruct = DllStructCreate("int64")
    DllStructSetData($hStruct, 1, -1 * ($iUsec * 10))
    DllCall($hDLL, "dword", "ZwDelayExecution", "int", 0, "ptr", DllStructGetPtr($hStruct))
EndFunc

Func _ShowClientArray()
    _ArrayDisplay($Clients)
EndFunc

 Client:

#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.14.5
 Author:         myName

 Script Function:
    Template AutoIt script.

#ce ----------------------------------------------------------------------------

; Script Start - Add your code below here
#include<File.au3>
#include <GUIConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <StringConstants.au3>
Global $VLCplaying = 0
Global $VLCTimestamp = 0
Global $VLCPID
Global $VLCconnected = 0
Global $Serverconnected = 0
Global $SocketServer = 0
Global $playandtimeA[2]

OnAutoItExitRegister (_exit)

$VLCPID = Run ('"C:\Program Files\VideoLAN\VLC\vlc.exe" --extraintf rc --rc-host localhost:2150')

sleep (2000)

TCPStartUp()

$Form1 = GUICreate("Form1", 379, 108, 192, 124)
$Input1 = GUICtrlCreateInput("127.0.2.1", 16, 72, 257, 21)
$Button1 = GUICtrlCreateButton("Connect", 280, 72, 89, 25)
$Label1 = GUICtrlCreateLabel("Not Connected", 16, 8, 36, 17)
GUISetState(@SW_SHOW)



While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $Button1
            _ConnectToServer ()
            _ConnectToPlayer ()

    EndSwitch
    if $VLCconnected AND $Serverconnected Then
        _SendLocalState ()
        _SyncLocalState ()
    EndIf
WEnd




_VLCsendandlisten ("")
_VLCsendandlisten ("")


Func _SendLocalState ()
    $timestamp = _VLCtimestamp ()
    $playing = _VLCplaystate ()
    TCPSend ($SocketServer,$playing & ":" & $timestamp & @CRLF)
    ConsoleWrite ($playing & ":" & $timestamp & @CRLF)
EndFunc

Func _GetGlobalState ()
    $playandtimeA[0] = 0
    $playandtimeA[1] = 0
    $playandtime = TCPRecv ($SocketServer,2048)
    $playandtimeA = StringSplit ($playandtime,":",2)
    Global $GlobalTimestamp = $playandtimeA[0]
    Global $GlobalPlaystate = $playandtimeA[1]
EndFunc

Func _SyncLocalState ()
    _GetGlobalState ()
    if $GlobalPlaystate = 1 Then
        _VLCplay ()
    ElseIf $GlobalPlaystate = 0 Then
        _VLCpause ()
    EndIf
    _VLCsettimestamp ($GlobalTimestamp)
EndFunc



Func _exit ()
    TCPShutdown ()
    ProcessClose ($VLCPID)
EndFunc


Func _ConnectToPlayer ()
    Global $SocketVLC=TCPConnect("127.0.0.1",2150)
    $VLCconnected = 1
    If $SocketVLC=-1 Then
        MsgBox(0,"Error","Failed to connect to VLC over tcp proctol on localhost.Is it running and set to listen for console commands on port 2150?")
        $VLCconnected = 0
    EndIf
EndFunc

Func _connectToServer ()
    Global $SocketServer = TCPConnect (GUICtrlRead ($Input1),8080)
    $Serverconnected = 1
    If $SocketServer=-1 Then
        MsgBox(0,"Error","Failed to connect to the Server, contact your master Chinx7 and giv moni.")
        $Serverconnected = 0
    EndIf
EndFunc



Func _VLCtimestamp ()
    _VLCsendandlisten ("get_time")
EndFunc

Func _VLCsettimestamp ($VLCTimestamp)
    _VLCsendandlisten ("seek " & $VLCTimestamp)
EndFunc


Func _VLCplay ()
    if _VLCplaystate = 0 Then
        _VLCsendandlisten ("play")
    EndIf
EndFunc

Func _VLCpause ()
    if _VLCplaystate = 1 Then
        _VLCsendandlisten ("play")
    EndIf
EndFunc


Func _VLCplaystate ()
    Return _VLCsendandlisten ("is_playing")
EndFunc



func _VLCsendandlisten ($commandsend)
    TCPSend($SocketVLC,$commandsend&@CRLF)
    $listen = TCPRecv($SocketVLC,4096);Check if it gets someting back and shows it in inputbox
Return $listen
EndFunc

 

Link to comment
Share on other sites

Hi Chinx7
My guess is that, sometimes, your variable $playandtime doesn't contain any separator ":"
Then the Array $playandtimeA is recreated with a single element [0], here :

$playandtimeA = StringSplit ($playandtime,":",2)

That's why you never get an error with $GlobalTimestamp = $playandtimeA[0]
But the error arrives immediately after with $GlobalPlaystate = $playandtimeA[1]

I suggest you add a test to detect that $playandtime contains a ":" each time the function is called, for example :

$playandtime = TCPRecv ($SocketServer,2048)
If StringInStr($playandtime, ":") = 0 Then
    MsgBox(0, "Error", "Separator : not found in string $playandtime")
EndIf

You'll tell us :)

Edited by pixelsearch
Link to comment
Share on other sites

Ok, I guess I need to look at some TCP-Tutorials, because I just don't get anything from the server,

Is the way I'm  trying to get information from the server by calling the TCPRecv in  _GetGlobalState even correct or do I have to change that?

But thanks to you I fixed another problem. I have to strip all whitespaces from what I'm sending and receiving, because the data I get is sometimes sprinkled with them.

I'll try to find the problems and report back.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...