Sign in to follow this  
Followers 0
frank10

script in W7 blocks randomly after some hours (in XP is ok)

19 posts in this topic

I made a script that runs 24/24 on a home server.

With a pc with XP all works well, but now I must switch to a pc with W7pro (i7 4 core) and here the script sometimes blocks itself and it's impossible to kill the process also from TaskManager (both the autoit and the wrapper)! I must reboot.

I use it to control from USB a series of Temperature sensors and relè (this, making Run from an exeternal exe in C), and from another USB board some other inputs.

I use also some includes: WinInet, sqlite, winapi and CommMg for the serial port. Finally I use also PacketX with WinPCAP to sniff a port.

I launch it from Scite because I want to see a lot of ConsoleWrite, the status of some sensors and relè.

I tried to run Scite in WinXPSp3 mode and in admin mode. It seems to be more stable, but sometimes it blocks anyway...

I tried also to put

#AutoIt3Wrapper_UseX64=N

at the beginning so I run Scite and autoit in 32 bit.

It can work 1-2days or only 1-2hours.

I really need to solve this as I need it to work well 24/24 and from XP it worked well, but now I really need W7.

What should I do?

Share this post


Link to post
Share on other sites



Well, it's very long code and difficult to follow. It's also a bit difficult to report because I must wait also 1-2days to see if it crashes or not.

I thought there can be general issues about XP-W7 that I don't know, so I asked.

For example, is it sufficient the XP compatibility mode in the properties of Scite.exe to emulate XP?

Is it sufficient to emulate 32bit with Autoit32 like it's executed in XP?

Some includes listed above or activeX like PacketX, could not work well in W7?

The fact it can't be killed the process with TaskManager, maybe is something hardware related, not SW. I interrogate a board every 300ms through CommMG, maybe in some circustances it doesn't respond anymore and blocks the port and the process?

I use this code for that part:

Func _SX16($comando,$releNum, $releStato)
if $comando = "rele" Then
  AdlibUnRegister("_ControlloManuale1")
  $bBinData = Binary(  "0x66010301" & $releNum & $releStato   )
elseif $comando = "uscite" Then
  AdlibUnRegister("_ControlloManuale1")
  $bBinData = Binary("0x660104010000") ; read 6 output
Else 
  $bBinData = Binary("0x660101010000") ; read 24 input
EndIf
$iNumbytes = BinaryLen($bBinData)
$tBinData = DllStructCreate("byte[" & $iNumbytes & "]")
DllStructSetData($tBinData, 1, $bBinData)
local $bit = '', $instr = ''
local $i = 1
While $i < 10
  If $i = 1 Then
   $iRet = _CommSendByteArray(DllStructGetPtr($tBinData), $iNumbytes, 1)
   If @error Or $iRet = -1 Then
    AdlibUnRegister("_ControlloManuale1") 
    ConsoleWrite("!Error: " & @error & @CRLF)
    _Commcloseport()
    Sleep(2000)
    _CommSetPort($CMPort, $sportSetError, $CmBoBaud, $CmboDataBits, $CmBoParity, $CmBoStop, $setflow, $RTSMode, $DTRMode)
    AdlibRegister("_ControlloManuale1", 300) 
   EndIf
  EndIf
  $instr &= Hex(_CommReadByte(),2)
  Sleep(20)
  $i += 1
WEnd
$instr = "0x" & StringMid($instr,StringInStr($instr,"55"),16)
  if $instr <> '' and  ($comando = "rele" or $comando = "uscite" ) Then
   $num = BinaryMid($instr,5,1)
   For $i = 0 To 7
    $hex = BitShift($num, $i)
    $b = BitAND($hex, 1)
    Select
     Case $i = 0 
       ......rest of script...........
    EndSelect
    $bit &= $b
   Next
  else  ;if  $instr <> '' and  $comando = "input"
    For $j = 0 To 2
     $num = BinaryMid($instr,4 +$j,1)
     For $i = 0 To 7
      $hex = BitShift($num, $i)
      $b = BitAND($hex, 1)
      $bit &= $b
      if $i = 7 Then $bit &= "-"
     Next
    Next
    $bit = StringLeft($bit,StringLen($bit)-1)
  
    For $i = 0 to 25
     if $i <> 9 or $i <> 18 Then
      Select
       Case $i = 0 
        ......rest of script......
       Case $i = 25 
      EndSelect
     EndIf
    Next
  EndIf
 
  $bit = ''
  if ($comando = "rele" or $comando = "uscite" ) Then AdlibRegister("_ControlloManuale1", 300)
EndFunc ; end _SX16

Every 300ms I call this Func and eventually stop the func with adlib _ControlloManuale1 because it has other call to the board and it could conflict.

In the check _CommSendByteArray, I tried to reinitialize the board in case of error, but it doesn't work.

Some rare times, when it blocks, I see the error message after the call to _CommSendByteArray, but it should set @error to 1, instead it is @error=0, but it hangs all.

From CommMG include:

; Returns:  on success returns 1
;          on failure returns -1 and sets @error to 1
;
;;NB could hang if byte cannot be sent and $iWaitComplete <> 0
;    could lose data if you send more bytes than the size of the outbuffer.
;    the output buffer size is 2048

Share this post


Link to post
Share on other sites

Use Opt("TrayIconDebug", 1) and hover over the icon when it's blocked to see what line it is hanging on. You may need to run the script non-compiled for this to work or to provide a useful line number.

Share this post


Link to post
Share on other sites

Thank you. Tomorrow I will try that option running the script from Scite.

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

You may also try:

1) Add logging to file especially suspicious parts of your code. After block/crash just look at last lines in your LOG file (to see what happen).

2) Use eliminating method: temporary remove some (suspicious) parts of code and see if it still blocks or no.

Edited by Zedna

Share this post


Link to post
Share on other sites

I launched Scite without compatibility mode nor Admin to provoke a faster block: in fact it was.

I made more ConsoleWrite in the func posted before, the suspected one called every 300ms:

ConsoleWrite("---SX-i:"&$i & "--")
While $i < 10
    If $i = 1 Then
        $iRet = _CommSendByteArray(DllStructGetPtr($tBinData), $iNumbytes, 1)
        ConsoleWrite("- iret:"&$iRet & " --")
        If @error = 1 Or $iRet = -1 Then
            AdlibUnRegister("_ControlloManuale1") 
            ConsoleWrite("!Error: " & @error & " iRet:" & $iRet & @CRLF)
            $ErrLog &= _NowCalc() & "   " & "ERR SX16 SendByteArray" & @CRLF
            _Commcloseport()
            Sleep(2000)
            _CommSetPort($CMPort, $sportSetError, $CmBoBaud, $CmboDataBits, $CmBoParity, $CmBoStop, $setflow, $RTSMode, $DTRMode)
            AdlibRegister("_ControlloManuale1", 300) 
        EndIf
    EndIf
    $instr &= Hex(_CommReadByte(),2)
    ConsoleWrite("----instr:"& $instr & " i:" &$i& @CRLF)
    Sleep(20)
    $i += 1
WEnd

the last log before crash:

---SX-i:1--- iret:1 ------instr:00 i:1

----instr:0055 i:2

----instr:005501 i:3

----instr:00550101 i:4

----instr:0055010120 i:5

----instr:005501012000 i:6

----instr:00550101200024 i:7

----instr:0055010120002400 i:8

----instr:005501012000240000 i:9

---SX-i:1--- iret:1 ------instr:00 i:1

----instr:0055 i:2

----instr:005501 i:3

----instr:00550104 i:4

----instr:0055010401 i:5

----instr:005501040100 i:6

----instr:00550104010000 i:7

----instr:0055010401000000 i:8

----instr:005501040100000000 i:9

---SX-i:1--

and IconTray says the block at line Sleep(20)

instead in Scite it hasn't displayed 2*ConsoleWrite before the sleep because has printed only ---SX-i:1-- that it's outside while loop ...

So the block should be at line

$iRet = _CommSendByteArray

like suspected before.

Why this func doesn't work well without admin privileges and compatibility mode in W7?

Another time I found another block in this func:

Func _io($comando)
$console = ''
$foo = Run('E:\___1-Wire\Domotica\io_ok.exe ' & $comando, @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While 1
  $line = StdoutRead($foo)
  If @error Then 
   ExitLoop
  EndIf
  $console = $console & $line
WEnd
While 1
  $line = StderrRead($foo)
  If @error Then 
   ExitLoop
  EndIf
WEnd
Sleep(1000)
EndFunc   ;==> end   _io

It calls an exe in C that returns a string. It blocks getting an infinite loop in the 2 while... Again only in W7 especially without admin and XP compatibility mode.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

You did several things wrong in your testing. First, never write output when testing a deadlock. Adding I/O can serialize your program on the I/O which may remove or change the location of the problem. Second, your enthusiasm for inserting ConsoleWrite() all over breaks your If @error test.

Run the script without all the stupid ConsoleWrite() statements and report which line it is hanging on.

Edited by Valik
Clarity.

Share this post


Link to post
Share on other sites

Ok, another block without any Consolewrite, in the second code of my previous post:

...

$line = StdoutRead($foo)

If @error Then

...

TrayIcon says at this @error

Now I'll repeat to see if blocks again at the SX16 func.

Share this post


Link to post
Share on other sites

It blocked again at the

Sleep(20)

of the other SX16 code I posted.

So there are only these two block points.

Share this post


Link to post
Share on other sites

Now, it blocks often also with Scite in compatibility mode...

One time the func with stdOutRead didn't returned the string from the external exe, but the script continued anyway.

Anyway when the script blocks, the external exe executed from Win console, works well.

So it seems not to be a problem of the exe, but of the autoit script not getting stdout from an external Run.

Maybe some problem with COM or IO?

The fact that trayDebug says it blocks at a Sleep() point what does it mean?

What do you think about these breaking points?

Share this post


Link to post
Share on other sites

Your code has a deadlock somewhere due to filling an I/O buffer. What version of AutoIt are you using? A couple of years ago stdout and stderr redirection was re-written so that it shouldn't deadlock even if you never read from the streams (assuming you have tons of memory available). It is still possible for stdin to deadlock.

You have not provided all of your code as far as I can tell. Also, as mentioned, you haven't told us what version of AutoIt you are using. Both are very important pieces of information.

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

I use autoit 3.3.6.1 but I tried also the 3.3.5.6.

I tried also the beta 3.3.7.21 but it fails with PacketX call, like I posted here:

I made a huge simplification of my script, keeping the main parts and all the IO and COM calls:

;~ #AutoIt3Wrapper_UseX64=n
;~ #AutoIt3Wrapper_Run_Debug_Mode= Y
#include <StaticConstants.au3>
#include <ProgressConstants.au3>
#include <GUIConstantsEx.au3>
#include <INet.au3>
#include <Date.au3>
#include <ftp.au3>
#include <misc.au3>
#include <Array.au3>
#include <Constants.au3>
#include <SQLite.au3>
#include <SQLite.dll.au3>
#include <IE.au3>
#include <string.au3>
#include <WinINet.au3>
#include <WinAPI.au3>  ; per processCalc
#include 'CommMG.au3' ;Include the Serial UDF:
Opt("TrayIconDebug", 1)
Global $Sx16Port = 4, $1_wireSeriale = "8s"
;Internal for the Serial UDF
Global $sportSetError = ''
;COM Vars
Global $CMPort = $Sx16Port ; Port
Global $CmBoBaud = 19200 ; Baud
Global $CmboDataBits = 8 ; Data Bits
Global $CmBoParity = "none" ; Paritys
Global $CmBoStop = 1 ; Stop
Global $setflow = 2 ; Flow
Global $RTSMode = 1
Global $DTRMode = 1
Global $iWait = 0
;Start up COM communication
local $a = _CommSetPort($CMPort, $sportSetError, $CmBoBaud, $CmboDataBits, $CmBoParity, $CmBoStop, $setflow, $RTSMode, $DTRMode)

;############################################# PacketX ----------
Const $PktXPacketTypePromiscuous = 0x0020
Const $PktXModeCapture = 1
; # CREATE PACKETX OBJECT #  
Global $oPktX = ObjCreate("PktX.PacketX")
If Not IsObj($oPktX) Then
MsgBox(48, "Error", "PacketX could not load the object, reinstall!")
$ErrLog &= _NowCalc() & "   " & "PacketX could not load the object, reinstall!" & @CRLF
Else
$EventObject = ObjEvent($oPktX, "PacketX_")
EndIf
; # GET THE DEVICE NUMBERS #
$deviceno = ""
$deviceitem = ""
For $i = 1 To $oPktX.Adapters.Count
If $oPktX.Adapters($i).IsGood Then
  $deviceitem = $i
  $deviceno = $deviceitem & "|" & $deviceno
Else
  MsgBox(48, "Error", "No Valid Network Adaptors was found!")
EndIf
Next
$interno = ''
; Start packet capture
_start()

AdlibRegister("_ControlloManuale1", 300) ; every 300ms do
; main loop:
While 1
If TimerDiff($CounterTimer) > $intervallo Then ; every 5 sec do:
   _SX16('uscite','','')
  _LeggiCounter()
  $CounterTimer = TimerInit()
EndIf

If Mod(@MIN, 5) = 0  Then ; every 5' do:
  ; there's another check to avoid more than one call at Mod(@MIN, 5):
  _io8()
  _LeggiTemp()
.......   
  _PHP_server("/public/Files/PHP/content.php", $JSONdata,0)
EndIf
Sleep(300)
WEnd
Func _io8()
$ioA = 0
While (Not IsArray($ioA))
  _io($1_wireSeriale & ' a')
  $ioA = StringSplit($console, ",")
  While Not (UBound($ioA) = 4)
   Sleep(100)
   _io($1_wireSeriale & ' a')
   $ioA = StringSplit($console, ",")
  WEnd
  if Not IsArray($ioA) Then Sleep(1000)
WEnd
Sleep(1000)
$ioB = 0
While (Not IsArray($ioB))
  _io($1_wireSeriale & ' b')
  $ioB = StringSplit($console, ",")
  While Not (UBound($ioB) = 4)
   Sleep(100)
   _io($1_wireSeriale & ' b')
   $ioB = StringSplit($console, ",")
  WEnd
  if Not IsArray($ioB) Then Sleep(1000)
WEnd
.........
EndFunc   ;==>_io8
Func _LeggiCounter()
While (Not IsArray($count))
  _counter($1_wireSeriale & ' a')
  $count = StringSplit($console, ",")
  While Not (UBound($count) = 5)
   _counter($1_wireSeriale & ' a')
   $count = StringSplit($console, ",")
  WEnd
WEnd
...........
_PHP_server("/public/Files/PHP/content.php", $JSONdata,0)
EndFunc   ;==>_LeggiCounter
Func _LeggiTemp()
_temperature($1_wireSeriale & " s f o n c i p x l t m") ;
...........
EndFunc   ;==>_LeggiTemp
Func _temperature($stanze)
$console = ''
$foo = Run('E:___1-WireDomoticatemperature.exe ' & $stanze, @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While 1
  $line = StdoutRead($foo)
  If @error Then
   ExitLoop
  EndIf
  $console = $console & $line
WEnd
While 1
  $line = StderrRead($foo)
  If @error Then
   ExitLoop
  EndIf
WEnd
EndFunc   ;==>_temperature
Func _counter($version)
$console = ''
$foo = Run('E:___1-WireDomoticacounter.exe ' & $version, @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While 1
  $line = StdoutRead($foo)
  If @error Then
   ExitLoop
  EndIf
  $console = $console & $line
WEnd
While 1
  $line = StderrRead($foo)
  If @error Then
   ExitLoop
  EndIf
WEnd
EndFunc
Func _io($comando)
$console = ''
$foo = Run('E:___1-WireDomoticaio.exe ' & $comando, @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While 1
  $line = StdoutRead($foo)
  If @error Then
   ExitLoop
  EndIf
  $console = $console & $line
WEnd
While 1
  $line = StderrRead($foo)
  If @error Then
   ExitLoop
  EndIf
WEnd
EndFunc

Func _ControlloManuale1()
  _SX16('','','')
  _ControlloManuale("")
EndFunc
Func _ControlloManuale($manual)
........
; check battery status in XP laptop:
$structSystemPowerStatus = DllStructCreate("byte ACLineStatus; byte BatteryFlag; byte BatteryLifePercent; byte Reserved1; dword BatteryLifeTime; dword BatteryFullLifeTime")
$ret = DllCall("kernel32.dll", "int", "GetSystemPowerStatus", "ptr", DllStructGetPtr($structSystemPowerStatus))
If @error Then  Consolewrite("Errore rilevamento gruppo continuità")
If $ret <> 0 Then   
  $nOnBattery   = DllStructGetData($structSystemPowerStatus,  "ACLineStatus")   
  $nBatteryLife = DllStructGetData($structSystemPowerStatus,  "BatteryLifePercent") 
  If $nOnBattery == 0 Then
   if Mod($conteggio, 10) = 0  Then   Consolewrite("Il portatile va a batteria, la percentuale Batt=" & $nBatteryLife  & @CRLF) 
  Else      
;~   MsgBox(48, @ScriptName, "Running on AC-power") 
  EndIf
EndIf
..............
if (@SEC = 30 or @sec = 0) Then
  ; async call to server:
  _PHP_server("/public/Files/PHP/loopOff.php", "" ,1)
  Sleep(2000)
  _PHP_server("/public/Files/PHP/contentLoop.php", "" , 1)
EndIf
EndFunc   ;==>_ControlloManuale
Func _SX16($comando,$releNum, $releStato)
if $comando = "rele" Then
  AdlibUnRegister("_ControlloManuale1")
  $bBinData = Binary(  "0x66010301" & $releNum & $releStato   )
elseif $comando = "uscite" Then
  AdlibUnRegister("_ControlloManuale1")
  $bBinData = Binary("0x660104010000") ; leggi 6 uscite
Else
  $bBinData = Binary("0x660101010000") ; leggi 24 ingressi
EndIf
$iNumbytes = BinaryLen($bBinData)
$tBinData = DllStructCreate("byte[" & $iNumbytes & "]")
DllStructSetData($tBinData, 1, $bBinData)
local $bit = '', $instr = ''
local $i = 1
While $i < 10
  If $i = 1 Then
   $iRet = _CommSendByteArray(DllStructGetPtr($tBinData), $iNumbytes, 1)
   If @error = 1 Or $iRet = -1 Then
    AdlibUnRegister("_ControlloManuale1")
    $ErrLog &= _NowCalc() & "   " & "ERR SX16 SendByteArray" & @CRLF
    _Commcloseport()
    Sleep(2000)
    _CommSetPort($CMPort, $sportSetError, $CmBoBaud, $CmboDataBits, $CmBoParity, $CmBoStop, $setflow, $RTSMode, $DTRMode)
    AdlibRegister("_ControlloManuale1", 300)
   EndIf
  EndIf
  $instr &= Hex(_CommReadByte(),2)
  Sleep(20)
  $i += 1
WEnd
$instr = "0x" & StringMid($instr,StringInStr($instr,"55"),16)
.........
if ($comando = "rele" or $comando = "uscite" ) Then AdlibRegister("_ControlloManuale1", 300)
EndFunc
;------------------PacketX ----------
Func _start()
; Select network adapter
$oPktX.Adapter = $oPktX.Adapters(2) ; W7    Broadcom10/100   $oPktX.Adapters (GUICtrlRead($Combo1))
; Set the Port to filter
$oPktX.Adapter.BPFilter = 'port 5060'
; Capture buffer parameters
$oPktX.Adapter.BuffSize = 256 * 1024 ; 256 KB
$oPktX.Adapter.BuffMinToCopy = 0
; Hardware filter and capture mode
$oPktX.Adapter.HWFilter = $PktXPacketTypePromiscuous
$oPktX.Adapter.Mode = $PktXModeCapture
$oPktX.Start
EndFunc   ;==>_start
Func PacketX_OnPacket($oPacket)
Dim $sline = ''
Local $tline = ''
For $bByte In $oPacket.Data
  If StringLen($sline) >= 48 Then
   $sline = ""
  EndIf
  If $bByte <= Chr(4) Then
   $tline = $tline & "0"
  EndIf
  $sline = $sline & Hex($bByte, 2) & " "
  $tline = $tline & Hex($bByte, 2) & " "
Next
If StringLen($sline) > 0 Then
  Local $wLine = ''
  Local $hesadecimale = StringSplit($tline, ' ')
  For $i = 1 To $hesadecimale[0]
   $wLine &= _HexToString($hesadecimale[$i])
  Next
..............
EndIf
EndFunc   ;==>PacketX_OnPacket
Func _PHP_server($url,$JSONdata, $async)
  Local $hInternetOpen, $hInternetConnect, $hHttpOpenRequest, $hHttpSendRequest
  _WinINet_Startup()
  If _WinINet_InternetAttemptConnect() Then
   $hInternetOpen = _WinINet_InternetOpen("Mozilla/5.0 Firefox/3.0.1", $INTERNET_OPEN_TYPE_DIRECT, 0, Default, Default)
   $hInternetConnect = _WinINet_InternetConnect($hInternetOpen, $INTERNET_SERVICE_HTTP, "www.myServer.com", 0, 0, Default, Default, 0)
   $type = "Content-Type: application/x-www-form-urlencoded" & @CRLF
   $agent = "User-Agent: Mozilla/5.0b" & @CRLF
   $toSend =  $type & $agent
   Local $hHttpOpenRequest = _WinINet_HttpOpenRequest($hInternetConnect, "POST", $url)
   $toSend &=  'Content-Length: ' & StringLen($JSONdata)
  
   if $async = 0  then
    local $hHttpSendRequest= _WinINet_HttpSendRequest($hHttpOpenRequest, $toSend, StringToBinary($JSONdata) )
   Else
    local $hHttpSendRequest= _WinINet_HttpSendRequestEx($hHttpOpenRequest, $toSend, StringToBinary($JSONdata) )
   EndIf
  
   $manual = ''
   $vReceived = Binary("")
   Do
    $vReceived &= _WinINet_InternetReadFile($hHttpOpenRequest, 1024)
   Until @error <> 0 Or Not @extended
   $vReceived = BinaryToString($vReceived)
  ...........
  EndIf
  _WinINet_InternetCloseHandle($hInternetOpen)
EndFunc   ;==>_PHP_server

I have COM calls to a serial port with CommMG through an USB adapter every 300ms,

I call an external exe that check sensors through a 1-wire network through an USB-serial-1-wire adapter every 5sec using stdOutRead to get a returning string,

finally some similar call to exe every 5min.

I send some little data every 5sec to a PHP server, too with Wininet.

I used AdLibRegister to make independent cycle on a board every 300ms, while the other board is called from the main while loop.

I made a func in adLibRegister that calls two other funcs, I don't know if it's possible to call them directly in a adLibRegister line...

In every case, if there is this filling buffer, why doesn't block on old laptop with XP?

EDIT: I never saw Autoit and Scite growing more than 90MB in TaskManager, with all testing Consolewrites, I have 6GB RAM, anyway.

Edited by frank10

Share this post


Link to post
Share on other sites

I made a huge simplification of my script, keeping the main parts and all the IO and COM calls:

It's still not very simple. I wonder how many times you must write the same Run()-StdoutRead()-StdErrRead() sequence before you think to factor that logic out into a function, for example.

I have COM calls to a serial port with CommMG through an USB adapter every 300ms,

The CommMG code is not present. To me that seems the most likely place for an error to be.

In every case, if there is this filling buffer, why doesn't block on old laptop with XP?

The are numerous reasons. A few are the OS may have a different buffer size or may not buffer at all. The hardware is different. The drivers are different.

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

CommMG:

#include-once
Opt("mustdeclarevars", 1) ;testing only
#cs
    UDF for commg.dll
    V1.0 Replaces mgcomm.au3
#ce
Const $sUDFVersion = 'CommMG.au3 V2.85'
Global $mgdebug = False
#cs
    Version 2.1.1 Added missing declarations which caused problems in scripts using Opt("MustDeclareVars",1) - thanks to Hannes
    Version 2.1 Thanks to jps1x2 for the read/send bte array incentive and testing.
    Version 2.0.2 beta changed readbytearray so returns no of bytes read
    Version 2.0.1 beta
    added _CommSendByteArray and _CommReadByteArray
    Version 2.0 - added _CommSwitch. Can now use up to 4 ports.
    Version 2.2 - add rts, dtr to setport
    added option for flow control = NONE to _CommSetPort
    version 2.3 use commg.dll v2.3 which allows any baud rate up to 256000.
    Version 2.4 added setTimeouts, SetXonXoffProperties
    Version 2.5 add _CommsetTimeouts, _CommSetXonXoffProperties
    Version 2.6 added _CommSetRTS, _CommSetDTR
    change switch so up to 50 com ports can be open at a time
    Version 2.7 added _CommSetDllPath
    Version 2.8 17th April 2010
    add GetLineStates for CTS, DSR, Ring Indicator and DCD
    Corrected return of functions _CommSetRTS and _CommSetDTR to be -1 on error
    Version 2.81 add sleep(20) in CommGetLine to reduce CPU usage. Thanks to jimg.
    Version 2.82 Correct error in _ComSetPort which could prevent _CommSetDllPath working.
    Version 2.83 add function _CommSetBufferSizes
    added ComGetPortNames around these versions but not sure when.
    Version 2.84 modify ReadByte to allow for error, probable cause is a timeout triggered by values set in _CommSetTimeouts
    Removed CloseDll in _CommClosePort so one port can be closed and others still used.
    Version 2.85 Fix error in _CommClosePort
    AutoIt Version: 3.2.3++
    Language:      English
    Description:    Functions for serial comms using commg2_4.dll or later
    Works with COM ports, USB to Serial converters, Serial to RS424 etc
    Functions available:
    _CommGetVersion
    _CommSetDllPath
    _CommListPorts
    _ComGetPortNames
    _CommSetPort
    _CommPortConnection
    _CommClearOutputBuffer
    _CommClearInputBuffer
    _CommGetInputcount
    _CommGetOutputcount
    _CommSendString
    _CommGetString
    _CommGetLine
    _CommReadByte
    _CommReadChar
    _CommSendByte
    _CommSendBreak; not tested!!!!!!!!!!
    _CommCloseport
    _CommSwitch
    _CommSendByteArray
    _CommReadByteArray
    _CommSetTimeouts
    _CommSetXonXoffProperties
    _CommSetRTS
    _CommSetDTR
    _CommGetLineStates
    _CommSetBufferSizes
    Author: Martin Gibson
#ce
#include-once
Global $fPortOpen = False
Global $hDll
Global $DLLNAME = 'commg.dll'
;===============================================================================
;
; Function Name:  _CommSetDllPath($sFullPath)
; Description:  Sets full path to th edll so that it can be in any location.
;
; Parameters:    $sFullPath -  Full path to the commg.dll e.g. "C:COMMScommg.dll"
; Returns;  on success 1
;          on error -1 if full path does not exist
;===============================================================================
Func _CommSetDllPath($sFullPath)
    If Not FileExists($sFullPath) Then Return -1
    $DLLNAME = $sFullPath
    Return 1
EndFunc   ;==>_CommSetDllPath

;===============================================================================
;
; Function Name:  _CommListPorts($iReturnType=1)
; Description:  Gets the list of available ports seperated by '|' or as an array
;
; Parameters:    $iReturnType -  integer:if $iReturnType = 1 then return a string with the list of COM ports seperated by '|'
;                                        if $iReturnType <> 1 then return an array of strings, with element [0] holding the number of COM ports
; Returns;  on success - a string eg 'COM1|COM8', or array eg ['2','COM1','COM2']
;          on failure - an empty string and @error set to 1 if dll could not list any ports
;                                           @error set to 2 id dll not open and couldn't be opened
;===============================================================================
Func _CommListPorts($iReturnType = 1)
    Local $vDllAns, $lpres
    If Not $fPortOpen Then
        $hDll = DllOpen($DLLNAME)
        If $hDll = -1 Then
            SetError(2)
            ;$sErr = 'Failed to open commg2_2.dll'
            Return 0;failed
        EndIf
        $fPortOpen = True
    EndIf
    If $fPortOpen Then
        $vDllAns = DllCall($hDll, 'str', 'ListPorts')
        If @error = 1 Then
            SetError(1)
            Return ''
        Else
            ;mgdebugCW($vDllAns[0] & @CRLF)
            If $iReturnType = 1 Then
                Return $vDllAns[0]
            Else
                Return StringSplit($vDllAns[0], '|')
            EndIf

        EndIf
    Else
        SetError(1)
        Return ''
    EndIf

EndFunc   ;==>_CommListPorts

;===============================================================================
;
; Function Name:  _Commgetversion($iType = 1)
; Description:  Gets the version of the dll if $iType = 1
;                  Or the version of this UDF if $iType = 2
; Parameters:    $iType - integer: = 1 to reurn the commg2_2.dll version
;                                 = 2 to return the UDF version
; Returns;  on success - a string eg 'V1.3'
;          on failure - an empty string and @error set to 1
;===============================================================================

Func _CommGetVersion($iType = 1)
    Local $vDllAns
    If $iType = 2 Then Return $sUDFVersion
    If $fPortOpen Then
        $vDllAns = DllCall($hDll, 'str', 'Version')
        If @error = 1 Then
            SetError(1)
            mgdebugCW('error in get version' & @CRLF)
            Return ''
        Else
            ;mgdebugCW('length of version is ' & stringlen($vDllAns[0]) & @CRLF)
            Return $vDllAns[0]
        EndIf
    Else
        $vDllAns = DllCall($DLLNAME, 'str', 'Version')
        If @error = 1 Then
            SetError(1)
            mgdebugCW('error in get version' & @CRLF)
            Return ''
        Else
            ;mgdebugCW('length of version is ' & stringlen($vDllAns[0]) & @CRLF)
            Return $vDllAns[0]
        EndIf
    EndIf

EndFunc   ;==>_CommGetVersion
;===============================================================================
;
; Function Name:  _CommSwitch($channel)
;switches functions to operate on channel 1, 2, 3 to 50
;returns  on succes the channel switched to ie 1 or 2
;        on failure -1
;Remarks  on start up of script channel 1 is selected, so if you only need one COM port
;        you don't need to use _CommSwitch
;        each channel needs to be set up with _CommSetPort
;        The same COM port cannot be used on more than one channel.
;When switch is used the first time on a channel number that port will be inactive
; and the port name will be '' (an empty string) until it is set with _CommSetport.
;The exception is that on creation channel 1 is always created and used as the
;port so switch is not needed unless more than one port is used.
;        The channel number is not related to the COM port number, so channel 1 can
;        be set to use COM2 and channel 4 can be set to use COM1 or any available port.
;Any channel number in the range 1 - 50 can be used, so it is possible to use
; the same channel number as the port number, ie switch(21) switches to COM21
;======================================================================================
Func _CommSwitch($channel)
    Local $vDllAns
    #cs remove section after fixing? comclose in dll
        If Not $fPortOpen Then
        SetError(1)
        Return 0
        EndIf
    #ce
    If $channel > 50 Then Return -1
    $vDllAns = DllCall($hDll, 'int', 'switch', 'int', $channel)
    If @error <> 0 Then
        SetError(1)
        Return -1
    Else
        mgdebugCW("COM port selected now is " & _CommPortConnection() & @CRLF)
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSwitch
;===========================================================================================================
;
; Function Name:    _CommSetport($iPort,ByRef $sErr,$iBaud=9600,$iBits=8,$ipar=0,$iStop=1,$iFlow=0,$RTSMode = 0,$DTRMode = 0)
;   Description:    Initialises the port and sets the parameters
;   Parameters: $iPort - integer = the port or COM number to set. Allowed values are 1 or higher.
;                        NB WIndows refers To COM10 Or higher`as .com10 but only use the number 10, 11 etc
;                 $sErr - string: the string to hold an error message if func fails.
;                 $iBaud - integer: the baud rate required. With commg.dll v2.3 and later any value allowed up to 256000.
;                          With v2.4 any value??
;                         If using commg.dll before V2.3 then only allowed values are one of
;                        50, 75, 110, 150, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 10400,
;                        14400, 15625, 19200, 28800, 38400, 56000, 57600, 115200, 128000, 256000
;                 $iBits - integer:  number of bits in code to be transmitted
;                 $iParity - integer: 0=None,1=Odd,2=Even,3=Mark,4=Space
;                 $iStop - integer: number of stop bits, 1=1 stop bit 2 = 2 stop bits, 15 = 1.5 stop bits
;                 $iFlow - integer: 0 sets hardware flow control,
;                                   1 sets XON XOFF control,
;                                   2 sets NONE i.e. no flow control.
;     $RTSMode 0= turns on the RTS line when the device is opened and leaves it on
;              1= RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) buffer is less than one-half full and lowers the RTS line when the buffer is more than three-quarters full.
;              2 = the RTS line will be high if bytes are available for transmission. After all buffered bytes have been sent, the RTS line will be low.
;              3 = turns off the RTS line when the port is opened and leaves it off
;    $DTRMode  0 = turns on the DTR line when the port is opened and leaves it on
;              1 = enables DTR handshaking
;                2 = disables the DTR line when the device is opened and leaves it disabled.
; Returns;  on success - returns 1 and sets $sErr to ''
;          on failure - returns 0 and with the error message in $sErr, and sets @error as follows
;                          @error            meaning error with
;                            1             dll call failed
;                            2             dll was not open and could not be opened
;                           -1             $iBaud
;                           -2             $iStop
;                           -4             $iBits
;                           -8             $iPort = 0 not allowed
;                          -16             $iPort not found
;                          -32             $iPort access denied (in use?)
;                          -64             unknown error
;Remarks     You cannot set the same COM port on more than one channel
;===========================================================================================================
Func _CommSetPort($iPort, ByRef $sErr, $iBaud = 9600, $iBits = 8, $iPar = 0, $iStop = 1, $iFlow = 0, $RTSMode = 0, $DTRMode = 0)
    Local $vDllAns
    $sErr = ''
    mgdebugCW("$fPortOpen = " & $fPortOpen & @CRLF)
    If Not $fPortOpen Then
        $hDll = DllOpen($DLLNAME)
        If $hDll = -1 Then
            SetError(2)
            $sErr = 'Failed to open commg.dll'
            Return 0;failed
        EndIf
        $fPortOpen = True
    EndIf
    mgdebugCW('port = ' & $iPort & ', baud = ' & $iBaud & ', bits = ' & $iBits & ', par = ' & $iPar & ', stop = ' & $iStop & ', flow = ' & $iFlow & @CRLF)
    $vDllAns = DllCall($hDll, 'int', 'SetPort', 'int', $iPort, 'int', $iBaud, 'int', $iBits, 'int', $iPar, 'int', $iStop, 'int', $iFlow, 'int', $RTSMode, 'int', $DTRMode)
    If @error <> 0 Then
        $sErr = 'dll SetPort call failed'
        SetError(1)
        Return 0
    EndIf
    If $vDllAns[0] < 0 Then
        SetError($vDllAns[0])
        Switch $vDllAns[0]
            Case -1
                $sErr = 'undefined baud rate'
            Case -2
                $sErr = 'undefined stop bit number'
            Case -4
                $sErr = 'undefined data size'
            Case -8
                $sErr = 'port 0 not allowed'
            Case -16
                $sErr = 'port does not exist'
            Case -32
                $sErr = 'access denied, maybe port already in use'
            Case -64
                $sErr = 'unknown error accessing port'
        EndSwitch
        Return 0
    Else
        Return 1
    EndIf
EndFunc   ;==>_CommSetPort

;===================================================================================
;
; Function Name:  _CommPortConnection()
; Description:  Gets the port connected to the selected channel - see _CommSwitch
; Parameters:    None
; Returns;  on success - a string eg 'COM5'
;          on failure - an empty string and @error set to the rerror set by DllCall
; Remarks - Can be used to verify the port is connected
;====================================================================================
Func _CommPortConnection()
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf

    $vDllAns = DllCall($hDll, 'str', 'Connection');reply is port eg COM8
    If @error <> 0 Then
        SetError(@error)
        Return ''
    Else
        Return $vDllAns[0]
    EndIf

EndFunc   ;==>_CommPortConnection

;=====================================================================================
;
; Function Name:  _CommSendString($sMGString,$iWaitComplete=0)
; Description:  Sends a string to the connected port on the currently selected channel
; Parameters:    $sMGString: the string to send sent without any extra CR or LF added.
;                $iWaitComplete- integer:0 = do not wait till string sent
;                                       1 = wait till sent
; Returns:  always 1
;          on success-  @error set to 0
;          on failure - @error set to the error returned from DllCall
;======================================================================================
Func _CommSendString($sMGString, $iWaitComplete = 0)
    ;sends $sMGString on the currently open port
    ;returns 1 if ok, returns 0 if port not open/active
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    If $sMGString = '' Then Return
    mgdebugCW("pre sendstring " & @CRLF)
    $vDllAns = DllCall($hDll, 'int', 'SendString', 'str', $sMGString, 'int', $iWaitComplete)
    If @error <> 0 Then
        mgdebugCW("past sendstring(1)" & @CRLF)
        SetError(@error)
        Return ''
    Else
        mgdebugCW("past sendstring(2)" & @CRLF)
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSendString

;================================================================================
;
; Function Name:  _CommGetstring()
; Description:  Get whatever characters are available received by the port for the selected channel
; Parameters:    none
; Returns:  on success the string and @error is 0
;          if input buffer empty then empty string returned
;          on failure an empty string and @error set to the error set by DllCall
; Notes: Use _CommGetLine to get a whole line treminated by @CR or a defined character.
;=================================================================================
Func _Commgetstring()
    ;get a string NB could be part of a line depending on what is in buffer
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    ;$sStr1 = ''
    ;$vDllAns = DllCall($hDll,'str','GetByte')
    $vDllAns = DllCall($hDll, 'str', 'GetString')
    If @error <> 0 Then
        SetError(1)
        mgdebugCW('error in _commgetstring' & @CRLF)
        Return ''
    EndIf
    Return $vDllAns[0]
EndFunc   ;==>_Commgetstring

;====================================================================================
;
; Function Name:  _CommGetLine($EndChar = @CR,$maxlen = 10000, $maxtime = 10000)
; Description:  Get a string ending in $EndChar
; Parameters:    $EndChar the character to indicate the end of the string to return.
;                    The $EndChar character is included in the return string.
;                $MaxLen - integer: the maximum length of a string before
;                                    returning even if $linEnd not received
;                                   If $maxlen is 0 then there is no max number of characters
;                 $maxtime - integer:the maximum time in mS to wait for the $EndChar before
;                                    returning even if $linEnd not received.
;                                   If $maxtime is 0 then there is no max time to wait
;
; Returns:  on success the string and @error is 0
;          If $maxlen characters received without the $lineEnd character, then these
;           characters are returned and @error is set To -1.
;          If $maxtime passes without receiving the $lineEnd character, then the characters
;           received so far are returned and @error is set To -2.
;          on failure returns any characters received and sets @error to 1
;======================================================================================
Func _CommGetLine($sEndChar = @CR, $maxlen = 0, $maxtime = 0)
    Local $vDllAns, $sLineRet, $sStr1, $waited, $sNextChar, $iSaveErr
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    $sStr1 = ''
    $waited = TimerInit()
    While 1;stringinstr($sStr1,$EndChar) = 0
        If TimerDiff($waited) > $maxtime And $maxtime > 0 Then
            SetError(-2)
            Return $sStr1
        EndIf

        If StringLen($sStr1) >= $maxlen And $maxlen > 0 Then
            SetError(-1)
            Return $sStr1
        EndIf
        ;$ic =  _CommGetInputCount()
        $sNextChar = _CommReadChar()
        $iSaveErr = @error
        If $iSaveErr = 0 And $sNextChar <> '' Then
            $sStr1 = $sStr1 & $sNextChar
            ;mgdebugCW($sStr1 & @CRLF)
            If $sNextChar = $sEndChar Then ExitLoop
        EndIf
        If $iSaveErr <> 0 And $iSaveErr <> 3 Then
            SetError(1)
            Return $sStr1
        EndIf
    WEnd

    Return $sStr1
EndFunc   ;==>_CommGetLine

;============================================================================
;
; Function Name:  _CommGetInputCount()
; Description:  Get the number of characters available to be read from the port.
; Parameters:    none
; Returns:  on success a string conversion of the number of characters.(eg '0', '26')
;          on failure returns an empty string and sets @error to 1
;===============================================================================
Func _CommGetInputCount()
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf

    $vDllAns = DllCall($hDll, 'str', 'GetInputCount')
    If @error <> 0 Then
        SetError(1)
        Return 0
    Else
        Return $vDllAns[0]
    EndIf

EndFunc   ;==>_CommGetInputCount

;============================================================================
;
; Function Name:  _CommGetOutputCount()
; Description:  Get the number of characters waiting to be sent from the port.
; Parameters:    none
; Returns:  on success a string conversion of the number of characters.(eg '0', '26')
;          on failure returns an empty string and sets @error to 1
;===============================================================================
Func _CommGetOutputCount()
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf

    $vDllAns = DllCall($hDll, 'str', 'GetOutputCount')
    If @error <> 0 Then
        SetError(1)
        Return ''
    Else
        Return $vDllAns[0]
    EndIf

EndFunc   ;==>_CommGetOutputCount

;================================================================================================
;
; Function Name:   _CommReadByte($wait = 0)
; Description:  Reads the byte as a string
; Parameters:    $wait:integer if 0 then if no data to read then return -1 and set @error to 1
;                              if <> 0 then does not return until a byte has been read
; Returns:  on success a string conversion of the value of the byte read.(eg '0', '20')
;          on failure
;                     if $wait is 0 returns an string and sets @error to 1 if no data to read
;                     if $wait is not 0 returns a string containing imnformation on the $error if know
;                                       and sets @error to 1.
;                     Returns empty string and sets @error to 2 ifDllCall failed
;
;;NB could hang if nothing rec'd when wait is <> 0
;==================================================================================================
Func _CommReadByte($wait = 0)
    Local $iCount, $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf

    If Not $wait Then
        $iCount = _CommGetInputCount()
        If $iCount = 0 Then
            SetError(1)
            Return ''
        EndIf
    EndIf
    $vDllAns = DllCall($hDll, 'str', 'GetByte')
    If @error <> 0 Then
        SetError(2)
        Return ''
    EndIf
    If StringLen($vDllAns[0]) > 1 Then
        Return SetError(3, 0, $vDllAns[0])
    EndIf
    Return $vDllAns[0]
EndFunc   ;==>_CommReadByte
;============================================================================
;
; Function Name:   _CommReadChar($wait = 0)
; Description:  Reads the next Character as a string
; Parameters:    $wait:integer if 0 then if no data to read then return -1 and set @error to 1
;                              if <> 0 then does not return until a byte has been read
; Returns:  on success a string of 1 character
;          on failure returns empty string and sets @error to 1
;
;
;;NB could hang if nothing rec'd when wait is <> 0
;===============================================================================
Func _CommReadChar($wait = 0)
    Local $sChar, $iErr
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    $sChar = _CommReadByte($wait)
    $iErr = @error
    If $iErr > 2 Then
        SetError(1)
        Return ''
    EndIf
    If $iErr == 0 Then Return Chr(Execute($sChar))
EndFunc   ;==>_CommReadChar

;============================================================================
; Function Name:   SendByte($byte,$iWaitComplete=0)
; Description:  Sends the byte value of $byte. $byte must be in range 0 to 255
; Parameters:    $byte the byte to send.
;                $iWaitComplete - integer: if 0 then functions returns without
;                                 waiting for byte to be sent
;                                 If <> 0 then waits till byte sent.
; Returns:  on success returns 1
;          on failure returns -1 and sets @error to 1
;
;;NB could hang if byte cannot be sent and $iWaitComplete <> 0
;===============================================================================
Func _CommSendByte($byte, $iWaitComplete = 0)
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    $vDllAns = DllCall($hDll, 'int', 'SendByte', 'int', $byte, 'int', $iWaitComplete)
    If @error <> 0 Then
        SetError(1)
        Return -1
    Else
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSendByte

;===============================================================================
; Function Name:   _CommSendByteArray($pAddr,$iNum,$iWait)
; Description:  Sends the bytes from address $pAddress
; Parameters:    $iNum the number of bytes to send.
;                $iWaitComplete - integer: if 0 then functions returns without
;                                 waiting for bytes to be sent
;                                if <> 0 then waits until all bytes are sent.
; Returns:  on success returns 1
;          on failure returns -1 and sets @error to 1
;
;;NB could hang if byte cannot be sent and $iWaitComplete <> 0
;   could lose data if you send more bytes than the size of the outbuffer.
;   the output buffer size is 2048
;===============================================================================
Func _CommSendByteArray($pAddr, $iNum, $iWait)
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    Local $vDllAns = DllCall($hDll, 'int', 'SendByteArray', 'ptr', $pAddr, 'int', $iNum, 'int', $iWait)
    If @error <> 0 Or $vDllAns[0] = -1 Then
        SetError(1)
        Return -1
    Else
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSendByteArray

;====================================================================================
; Function Name:   _CommReadByteArray($pAddr,$iNum,$iWait)
;
; Description:  Reads bytes and writes them to memory starting at address $pAddress
; Parameters:    $iNum the number of bytes to read.
;                $iWaitComplete - integer: if 0 then the functions returns
;                                  with the available bytes up to $iNum.
;                                 if 1 then waits until the $iNum bytes received.
; Returns:  on success returns the Number of bytes read.
;          on failure returns -1 and sets @error to 1
;
;;NB could hang if bytes are not received and $iWaitComplete <> 0
;   the input buffer size is 4096
;====================================================================================
Func _CommReadByteArray($pAddr, $iNum, $iWait)
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    Local $vDllAns = DllCall($hDll, 'int', 'ReadByteArray', 'ptr', $pAddr, 'int', $iNum, 'int', $iWait)
    If @error <> 0 Or $vDllAns[0] = -1 Then
        SetError(1)
        Return -1
    Else
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommReadByteArray

;===============================================================================
; Function Name:   ClearOutputBuffer()
; Description:  Clears any characters in the out put queue5
; Parameters:    none
; Returns:  on success returns 1
;          on failure returns -1 and sets @error to 1
;
;===============================================================================
Func _CommClearOutputBuffer()
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    Local $vDllAns = DllCall($hDll, 'int', 'ClearOutputBuffer')
EndFunc   ;==>_CommClearOutputBuffer
Func _CommClearInputBuffer()
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    Local $vDllAns = DllCall($hDll, 'int', 'ClearInputBuffer')
    If @error <> 0 Then
        Return -1
    Else
        Return 1
    EndIf
EndFunc   ;==>_CommClearInputBuffer

;===============================================================================
; Function Name:   ClosePort()@@@@@@@@@to be improved or replaced. Should have parameter for channel to close and new fn for closedown
; Parameters
;           TODO ?? $fAll if set to true or non zero then
; Description:  closes currently selected port
; Remarks:
;
; Parameters:    none
; Returns:  no return value
;===============================================================================
Func _CommClosePort($fAll = false)
mgdebugCW("Closing port" & @CRLF)
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    _CommClearOutputBuffer()
    _CommClearInputBuffer()
    DllCall($hDll, 'int', 'CloseDown')
    If @error <> 0 Then ConsoleWrite("Error closing dll" & @CRLF)
    If $fAll Then
mgdebugCW("Closing port step 2" & @CRLF)
        DllClose($hDll)
mgdebugCW("Closing port step 3" & @CRLF)
        $fPortOpen = False
    EndIf
EndFunc   ;==>_CommClosePort

;================================================================================================
; Function Name:   SendBreak($iDowTime,$iUpTime)
; NB Simulates the break signal used by some equipment to indicate the start of a sequence
;    Not tested so might Not work. Any feedback welcome - PM martin on Autoit forum
; Description:  sets the TX line low for $iDowTime, then sets it high for $iUpTime
; Parameters:    $iDowTime - integer: the number of ms to hold the TX line down
;                $iUpTime   - integer: the number of ms to hold the line up for before returning
;                 if $iDowTime or $iUpTime is zero then does nothing and returns
; Returns:  on success returns 1
;          on failure returns 0 and sets @error to
;                                         = 1 if one of params is zero
;                                         = 2 1 unable to use the DLL file,
;                                         = 3 unknown "return type" from dll
;                                         = 4 "function" not found in the DLL file.
; Notes : Not tested!
;================================================================================================
Func _CommSendBreak($iDowTime, $iUpTime);requirescommg2_2.dllv2.0 or later
    Local $vDllAns
    If $iDowTime = 0 Or $iUpTime = 0 Then
        SetError(1)
        Return 0
    EndIf
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    $vDllAns = DllCall($hDll, 'int', 'SendBreak', 'int', $iDowTime, 'int', $iUpTime)
    If @error <> 0 Then
        SetError(@error + 1)
        Return 0
    Else
        ;mgdebugCW('done setbreak' & @CRLF)
        Return 1;success
    EndIf
EndFunc   ;==>_CommSendBreak

;=========== _CommSetBufferSizes ========================================================================================================
; Description: Sets the buffer sizes for the current channel
;Parameters - $InputLen - the maximum number of bytes which can be received and waiting to be read
;          - $OutPutLen   - the number of bytes which can be qued waiting to be transmitted;
;Return 1 on success
;      0 on failure
;=============================================================================================================================================================
Func _CommSetBufferSizes($InPutLen, $OututLen = 2048)
    Local $vDllAns = DllCall($hDll, 'int', 'SetBufSizes', 'int', $InPutLen, 'int', $OututLen)
    If @error <> 0 Then
        SetError(@error + 1)
        Return 0
    Else
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSetBufferSizes
;=========== _CommSetTimeouts ========================================================================================================
; Description: Sets the timeouts for the current channel
;Parameters - ReadInt   - maximum time allowed to elapse between the arrival of two characters
;          - ReadMult   - multiplier used to calculate the total timeout period for read operations.
;                         For each read operation, this value is multiplied by the requested number of bytes to be read.
;          - ReadConst  - constant used to calculate the total timeout period for read operations.
;                         For each read operation, this value is added to the product of the ReadMultiplier member and the requested number of bytes.
;          - WriteMult  - multiplier used to calculate the total timeout period for write operations.
;                         For each write operation, this value is multiplied by the number of bytes to be written.
;          - WriteConst - constant used to calculate the total time-out period for write operations.
;                         For each write operation, this value is added to the product of the WriteMultiplier member and the number of bytes to be written.
; if a parameter is set to 0 it means that timeout will not be used. All values are at zero when a port is opened.
; Return 1 on success
;       0 on failure
;=============================================================================================================================================================
Func _CommSetTimeouts($ReadInt = 0, $ReadMult = 0, $ReadConst = 0, $WriteMult = 0, $WriteConst = 0)
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return 0
    EndIf
    $vDllAns = DllCall($hDll, 'int', 'SetTimeouts', 'int', $ReadInt, 'int', $ReadMult, 'int', $ReadConst, 'int', $WriteMult, 'int', $WriteConst)
    If @error <> 0 Then
        SetError(@error + 1)
        Return 0
    Else
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSetTimeouts

;====================== SetXonXoffProperties =======================================================================
; Description: Set the values used for the XON and XOFF characters, and when these charactyers are to be transmitted
; Parameters - $XonChar - the ASCII code for the character to be sent to indicate the port is ready to receive
;           - $XoffChar - the ASCII code fo rthe character to be sent to stop receiving
;           - $XonStart - when the number of characters in the input buffer falls below this value the XonChar will be sent
;           - $XoffStop - when the number of bytes free in the input buffer falls below this value the XoffChar will be sent
;When a port is opened the values are as the defaults for the function.
;Return - on success 1
;      - on error   0 if error making dllcall and @error set to 1
;                  -1  illegal XonChar value
;                  -2  illegal XoffChar value
;
Func _CommSetXonXoffProperties($XonChar = 0x11, $XoffChar = 0x13, $XonStart = 0, $XoffStop = 0)
    Local $vDllAns
    If $XonChar > 255 Or $XonChar < 0 Then Return -1
    If $XoffChar > 255 Or $XoffChar < 0 Then Return -2

    $vDllAns = DllCall($hDll, 'int', 'SetXonXoffProperties', 'byte', $XonChar, 'byte', $XoffChar, 'int', $XonStart, 'int', $XoffStop)
    If @error <> 0 Then
        SetError(@error + 1)
        Return 0
    Else
        Return $vDllAns[0]
    EndIf
EndFunc   ;==>_CommSetXonXoffProperties
Func mgdebugCW($sDB)
    If Not $mgdebug Then Return
    ConsoleWrite($sDB)
EndFunc   ;==>mgdebugCW
;===================================================================================
;
; Function Name:  _CommSetRTS()
; Description:  Sets or restets the RTS signal for to the selected channel - see _CommSwitch
; Parameters:    $iSet = 1 to set 0 to reset
; Returns;  1 on success
;          on failure -1 and @error set to 1
; Notes  Only works if flow control is set to NONE or XON/XOFF. Ie not for hardware handshaking.
;====================================================================================
Func _CommSetRTS($iSet)
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return -1
    EndIf

    $vDllAns = DllCall($hDll, 'int', 'SetRTS', 'int', $iSet)
    If @error <> 0 Then
        SetError(1)
        Return -1
    Else
        Return 1
    EndIf

EndFunc   ;==>_CommSetRTS

;===================================================================================
;
; Function Name:  _CommSetDTR()
; Description:  Sets or restets the DTR signal for to the selected channel - see _CommSwitch
; Parameters:    $iSet = 1 to set 0 to reset
; Returns;  1 on success
;          on failure -1 and @error set to 1
; Notes  Only works if flow control is set to NONE or XON/XOFF. Ie not for hardware handshaking.
;====================================================================================
Func _CommSetDTR($iSet)
    Local $vDllAns
    If Not $fPortOpen Then
        SetError(1)
        Return -1
    EndIf

    $vDllAns = DllCall($hDll, 'int', 'SetDTR', 'int', $iSet)
    If @error <> 0 Then
        SetError(1)
        Return -1
    Else
        Return 1
    EndIf

EndFunc   ;==>_CommSetDTR
;===================================================================================
;
; Function Name:  _CommGetLIneStates()
; Description:  Gets the states of 4 signals
; Parameters:    none
; Returns;   on success
;              returns an array with 4 elements giving the state of the lines CTS, DSR, Ring Indicator and DCD
;              in that order. Value True = ON, value False = OFF
;          on failure
;           returns -1 and @error set to 1
;====================================================================================
Func _CommGetLineStates()
    Local $vDllAns
    Local $iL, $aStates[4]
    If Not $fPortOpen Then
        SetError(1)
        Return -1
    EndIf

    $vDllAns = DllCall($hDll, 'int', 'GetLineStates')
    If @error <> 0 Then
        SetError(1)
        Return -1
    EndIf
    ConsoleWrite($vDllAns[0] & @CRLF)
    For $iL = 0 To 3
        $aStates[$iL] = BitAND($vDllAns[0], 2 ^ $iL) <> 0
    Next
    Return $aStates
EndFunc   ;==>_CommGetLineStates

; =====================================================================================================================
; Name........................: _ComGetPortNames
; Description ................: Lists all com ports or a single com port and the names to a 2D array
; Syntax......................: _GetComPorts($sComPort = 0)
; Parameters .................: $sComPort - Either 0 or "" for all ports or a number for a particular port, eg 12 or a string "COM12"
; Return on success ..........:  an array (size depends on $sComPort)
; Return on Failure ..........: an empty string "" and sets @error to
;                                 -1 incorrect parameter
;                                 1 no COM Ports found
;                                 2 specified COM port not found.
; Author .....................: funkey, 2010, Nov 29th
; Modified....................: by martin and renamed from  _GetComPorts to _ComGetPortNames, 14th December 2010
; Remarks ....................:
; Related ....................:
; Link to original function...: http://www.autoitscript.com/forum/topic/122663-getcomports/page__view__findpost__p__851620
; Example ....................: Yes see below
;
; ========================================================================================================================

Func _ComGetPortNames($sComPort = "")
    Local $objWMIService, $colItems, $stempName, $aTemp, $sRet, $iCount
    If IsInt($sComPort) Then
        If $sComPort = 0 Then
            $sComPort = ""
        Else
            $sComPort = "COM" & $sComPort
        EndIf
    EndIf

    If $sComPort <> "" And Not StringIsInt(StringReplace($sComPort, "COM", "")) Then Return SetError(-1, 0, "")
    $objWMIService = ObjGet("winmgmts:localhostrootCIMV2")
    $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%" & StringReplace($sComPort, "COM", "") & ")'", "WQL", 48)
    For $objItem In $colItems
        $sRet &= $objItem.Name & @CR
    Next
    If $sRet = "" Then Return SetError(1, 0, "")
    If $sComPort <> "" And Not StringInStr($sRet, "(" & $sComPort & ")") Then Return SetError(2, 0, "")

    $aTemp = StringSplit($sRet, @CR, 2)
    Dim $aRet[UBound($aTemp) - 1][2]
    $iCount = 0
    For $i = 0 To UBound($aTemp) - 2
        $stempName = StringTrimLeft($aTemp[$i], StringInStr($aTemp[$i], "(", 0, -1) - 1);StringTrimRight(, 1)
        If $sComPort = "" Or $stempName = "(" & $sComPort & ")" Then
            $aRet[$i][0] = StringTrimRight(StringTrimLeft($stempName, StringInStr($stempName, "(", 0, -1)), 1)
            $aRet[$i][1] = StringLeft($aTemp[$i], StringInStr($aTemp[$i], "(", 0, -1) - 2)
            $iCount += 1
        EndIf
    Next
    ReDim $aRet[$iCount][2]
    Return $aRet
EndFunc   ;==>_ComGetPortNames
;=======================================================================================================================

#cs =============_ComGetPortNames example start===============================
    #include <array.au3>
    Local $aComPort = _ComGetPortNames()
    _ArrayDisplay($aComPort)
    Local $sComPort = _ComGetPortNames("COM1")
    If @error Then
    MsgBox(16, "Error " & @error, "No matching COM port found.")
    Else
    ConsoleWrite($sComPort & @CRLF)
    EndIf
#ce =============_GetComPorts example end===============================
Opt("MustDeclareVars", 0)
Edited by frank10

Share this post


Link to post
Share on other sites

But if there is a filling buffer with the same code that receive always the same quantity of data, why is blocking randomly?

It should block everytime after some precise hours, instead it can be within 1hour-1day.

the only thing variable could be the StderrRead, due to communication errors in 1-wire network and this could happen randomly, I think.

Or maybe the PacketX sniffing port.

Share this post


Link to post
Share on other sites

All I can suggest is start removing pieces of code until the deadlock goes away. Once you isolate what you think is the deadlock then start with that bit of code and see if it locks. If it doesn't start adding code back in until it does.

Share this post


Link to post
Share on other sites

Thank you, I will try to see.

At the moment I made a second script to check if the blocking script is blocked, to close the process and running it again. I made a counter variable exposed to the other script in the window title to detect the blocked script.

Is there some simpler method to detect if a script is active but blocked?

Share this post


Link to post
Share on other sites

I think I found what is blocking: it's stdOutRead, like it was suspected.

I made this test script:

#RequireAdmin
#include <Constants.au3>
Opt("TrayIconDebug", 1)
global $foo, $console, $line

while 1
Sleep(1000)
$console = ''
$foo = Run('F:\testSTD.exe ', @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
_checkSTD()
if Mod(@min , 1)= 0 and @sec = 0 Then ConsoleWrite(@hour & ":" &@min & @CRLF)
WEnd

Func _checkSTD()
$line = ''
While 1
  $line = StdoutRead($foo)
  If @error Then
   ExitLoop
  EndIf
  $console = $console & $line
Wend
EndFunc

my test C code (I attach also the exe):

int main(short argc, char **argv)
{
       printf("00111000;00111000;01000000");
}

Resuming my rig:

W7 64bit on two different pc i7 and i3

autoit 3.3.6.1

running autoit in 32bit with admin and compatibility mode XP SP3 but also without them.

it blocks after about 30' .

It remains blocked inside the while loop of _checkSTD() because it hasn't received the @error to exit but the external exe was closed. Also the CPU occupation rises at 25% while normally is at 1%.

Another time it blocked at the Sleep(1000).

the script RAM always grows up while script is running, but it doesn't block at the same amount.

Why does the script Ram grow? The filling buffer?

Share this post


Link to post
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
Sign in to follow this  
Followers 0