@ECHO OFF
GOTO start
****************************************************************************
* (C) KEBA - All rights reserved
****************************************************************************
* DESCRIPTION:
*    Download firmware to P30 and P20
*    - look up firmware file in current working directory
*    - discover connected devices
*    - program all found devices that fit to provided firmware file
*---------------------------------------------------------------------------
* or130201 v1.00
*   first version
*
* or130204 v1.10
*   use local network interface address
*
* or130301 v1.20
*   - handle ethernet link down
*   - specify alternative Stellaris MAC if MAC is not yet programmed
*
* or130517 v1.30
*   additional low-level device discovering
*
* or130917 v1.31
*   log version info before download
*
* or131104 v1.40
*   additional command line args to
*   - use LoTAP oucflash instead of e2flash
*   - exit without pause
*
* or131211 v1.50
*   preserve P20 discovery results after download in report.txt
*
* or140128 v1.60
*   - using new e2flash v11
*     - high-level device discovery accepts devices in foreign sub-networks
*       - requires at least fw v1.013a8 (locator response is broadcasted)
*     - adjust device IP adress fitting to local adapter's IP address
*   - print out e2flash version info only at the beginning
*   - do low-level discovery at any rate to find device without correct fw too
*   - output of found devices more readable formatted
*   - max. two dicovery cycles at the beginning
*
* or140204 v1.61
*   displaying of "No device(s) found" error message fixed
*
* or140210 v1.62
*   successful discover sets return code to EXIT_SUCCESS
*
* or140416 v1.70
*   - New option /S (strict discovery) to accept high-level discovered devices
*     in same sub-network only.
*     !!! This new feature requires at least e2flash/oucflash v12 !!!
*   - CLS (clear screen) at start of script removed.
*
* or150217 v1.71
*   - exclude locator demon from firmware download
*   - additional command line arg to omit low-level device discovery
*
* or150916 v2.00
*   - renamed to p30flash.cmd
*   - compatible to p20flash.cmd
*   - but verifies that firmware file matches the PDC device
*     (downloads P30 firmware only to P30 wallbox, P20 firmware to P20)
*
* or151021 v2.10
*   - P30 bootloader uses new BOOTP(55167) and TFTP(55169) server ports.
*   - To program these P30 devices, an e2flash.exe of at least version 16 is necessary.
*
* or151112 v2.20
*   Using different alternative MAC addresses for P20 and P30 devices
*   as default MAC addresses are different between these devices.
*
****************************************************************************
:start
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
REM CLS  removed to preserve output before this script starts
SET SCRIPT_VERSION=P30 flash utility v2.20 (C) KEBA AG Linz, Austria
ECHO !SCRIPT_VERSION!

REM exit codes
SET EXIT_SUCCESS=0
SET EXIT_USAGE=1
SET EXIT_NOENT=2
SET EXIT_FAULT=14
SET EXIT_NODEV=19

REM constants
SET SCRIPT_NAME=p30flash.cmd
SET FLASH_EXE=e2flash.exe
SET LOG_FILE=p30flash.log
SET TMP_FILE=p30flash.tmp
SET REP_FILE=report.txt
SET DEF_FW_FILE_WILD_ARGS=kec_pdc*.bin

REM globals
SET RC=!EXIT_SUCCESS!
SET FW_FILE_WILD_ARGS=!DEF_FW_FILE_WILD_ARGS!
SET LOADER=
SET FW_FILE=
SET VERBOSE=
SET COUNT=
SET STRICT=
SET DEVICE_FOUND=
SET SUPPRESS_CONFIRM=
SET DEV_FLASHED=
SET PAUSE_ON_EXIT=y
SET START_WITH_NEW_REPORT=
SET OMIT_LOW_LEVEL_DISCOVERY=
SET PDC_TYPE=

IF EXIST !FLASH_EXE! (
    FOR /F "usebackq tokens=*" %%i IN (`!FLASH_EXE! --version`) DO (
        SET LOADER=%%i
        GOTO showLoader
    )
) ELSE (
    CALL :error Loader !FLASH_EXE! not found
    SET RC=!EXIT_FAULT!
    GOTO end
)
:showLoader
ECHO Using !LOADER!

REM parse command line (for loop doesn't return /? argument)
:parseCmdLine
SET ARG=%~1
IF DEFINED ARG (
    CALL :onCmdLineArg
    IF !RC! EQU !EXIT_SUCCESS! (
        SHIFT
        GOTO parseCmdLine
    ) ELSE (
        GOTO end
    )
)

REM detect firmware file
:detectFwFile
FOR %%i IN ( "!FW_FILE_WILD_ARGS!" ) DO (
    SET "FW_FILE=%%~i"
)
IF DEFINED FW_FILE (
    IF EXIST "!FW_FILE!" (
        FINDSTR /C:"KEBA P20" "!FW_FILE!" >NUL
        IF ERRORLEVEL 1 (
            FINDSTR /C:"KEBA P30" "!FW_FILE!" >NUL
            IF ERRORLEVEL 1 (
                CALL :error Invalid firmware file: !FW_FILE!
                SET RC=!EXIT_USAGE!
                GOTO end
            ) ELSE (
                SET PDC_TYPE=P30
            )
        ) ELSE (
            SET PDC_TYPE=P20
        )
        CALL :seperator1
        ECHO Using !PDC_TYPE! firmware file '!FW_FILE!'
    ) ELSE (
        CALL :error Firmware file not found: !FW_FILE!
        SET RC=!EXIT_NOENT!
        GOTO end
    )
) ELSE (
    CALL :error Firmware file is missing
    CALL :usage
    SET RC=!EXIT_USAGE!
    GOTO end
)

REM discover connected devices
CALL :seperator1
ECHO Searching for connected devices ...
ECHO.
FOR /L %%i IN ( 1, 1, 2 ) DO (
    IF EXIST !LOG_FILE! DEL !LOG_FILE!
    SET RC=!EXIT_SUCCESS!
    CALL :discover
    IF DEFINED DEVICE_FOUND (
        REM do a low-level discovery too to find devices without correct firmware
        SET RC=!EXIT_SUCCESS!
        IF NOT DEFINED OMIT_LOW_LEVEL_DISCOVERY CALL :discover_ll
    ) ELSE IF !RC! EQU 5 (
        REM high-level discovery did not find devices
        SET RC=!EXIT_SUCCESS!
        IF NOT DEFINED OMIT_LOW_LEVEL_DISCOVERY CALL :discover_ll
    )
    IF DEFINED DEVICE_FOUND (
        ECHO Found devices:
        CALL :printOutDevices
        GOTO program
    ) ELSE IF !RC! NEQ 5 (
        SET CONTINUE=!SUPPRESS_CONFIRM!
        IF NOT DEFINED CONTINUE (
            SET /P CONTINUE="Continue ([y]/n): "
        )
        IF "!CONTINUE!" == "N" GOTO done
        IF "!CONTINUE!" == "n" GOTO done
    )
)

:done
IF NOT DEFINED DEVICE_FOUND (
    CALL :error No !PDC_TYPE! device^(s^) found
    SET RC=!EXIT_NODEV!
)
GOTO end

REM program connected devices
:program
CALL :seperator1
ECHO Start programming of found devices ...
FOR /F "tokens=1,2,3,*" %%i IN ( !LOG_FILE! ) DO (
    SET MAC=%%i
    SET REM_IP=%%j
    SET LOC_IP=%%k
    SET VERINFO=%%l
    SET RC=!EXIT_SUCCESS!
    CALL :download
    IF !RC! NEQ !EXIT_SUCCESS! GOTO end
)
GOTO end


REM ----------------------------
REM functions
REM ----------------------------
:discover
    !FLASH_EXE! -d1 !COUNT! !STRICT! >!TMP_FILE!
    IF ERRORLEVEL 1 (
        SET RC=!ERRORLEVEL!
    ) ELSE IF DEFINED PDC_TYPE (
        FINDSTR !PDC_TYPE! !TMP_FILE! >>!LOG_FILE!
        IF ERRORLEVEL 1 (
            REM no fitting devices found
            SET RC=5
        ) ELSE (
            REM fitting devices found
            SET RC=!EXIT_SUCCESS!
            SET DEVICE_FOUND=1
        )
    ) ELSE (
        REM any devices found
        SET RC=!EXIT_SUCCESS!
        SET DEVICE_FOUND=1
    )
    IF EXIST !TMP_FILE! DEL !TMP_FILE!
    GOTO :eof

:discover_ll
    IF "!PDC_TYPE!"=="P30" (
        REM use new port set
        !FLASH_EXE! -p2 -d0 !COUNT! >>!LOG_FILE!
    ) ELSE (
        REM use original port set
        !FLASH_EXE! -p1 -d0 !COUNT! >>!LOG_FILE!
    )
    IF ERRORLEVEL 1 (
        SET RC=!ERRORLEVEL!
    ) ELSE (
        SET RC=!EXIT_SUCCESS!
        SET DEVICE_FOUND=1
    )
    GOTO :eof

:download
    CALL :seperator2
    CALL :printOutDevice
    REM exclude locator demon from firmware download
    ECHO.!VERINFO! | findstr /C:Locator 1>nul 2>&1
    IF ERRORLEVEL 1 (
        SET DO_FLASH=!SUPPRESS_CONFIRM!
    ) ELSE (
        SET DO_FLASH=N
        ECHO.
        ECHO is not supported
    )
    IF NOT DEFINED DO_FLASH (
        ECHO.
        SET /P DO_FLASH="Program this device ([y]/n): "
    )
    IF /I "!DO_FLASH!" == "N" GOTO :eof
    IF "!PDC_TYPE!"=="P30" (
        REM use new port set
        !FLASH_EXE! !VERBOSE! -p2 -m!MAC! -i!REM_IP! -l!LOC_IP! -a00:1A:B6:00:64:00 "!FW_FILE!"
    ) ELSE (
        REM use original port set
        !FLASH_EXE! !VERBOSE! -p1 -m!MAC! -i!REM_IP! -l!LOC_IP! -a00:1A:B6:00:02:74 "!FW_FILE!"
    )
    IF ERRORLEVEL 1 (
        SET RC=!EXIT_FAULT!
    ) ELSE (
        SET DEV_FLASHED=true
    )
    ECHO.
    GOTO :eof

:error
    ECHO.[ERR] %*
    ECHO.
    GOTO :eof

:onCmdLineArg
    IF "!ARG!" == "/?" (
        SET RC=!EXIT_USAGE!
    ) ELSE IF /I "!ARG:~0,2!" == "/C" (
        CALL :onOpt_C
    ) ELSE IF /I "!ARG!" == "/L" (
        SET FLASH_EXE=oucflash.exe
    ) ELSE IF /I "!ARG!" == "/N" (
        SET START_WITH_NEW_REPORT=y
    ) ELSE IF /I "!ARG!" == "/O" (
        SET OMIT_LOW_LEVEL_DISCOVERY=y
    ) ELSE IF /I "!ARG!" == "/S" (
        SET STRICT=-s
    ) ELSE IF /I "!ARG!" == "/V" (
        SET VERBOSE=--verbose
    ) ELSE IF /I "!ARG!" == "/X" (
        SET PAUSE_ON_EXIT=
    ) ELSE IF /I "!ARG!" == "/Y" (
        SET SUPPRESS_CONFIRM=y
    ) ELSE IF /I "!ARG!" == "/-Y" (
        SET SUPPRESS_CONFIRM=
    ) ELSE IF "!ARG:~0,1!" == "/" (
        CALL :error Invalid option: !ARG!
        SET RC=!EXIT_USAGE!
    ) ELSE (
        SET "FW_FILE_WILD_ARGS=!ARG!"
    )
    GOTO :eof

:onOpt_C
    SET NUM=!ARG:~3!
    IF DEFINED NUM (
        SET COUNT=-c!NUM!
    ) ELSE (
        CALL :error argument is missing: /C
        SET RC=!EXIT_USAGE!
    )
    GOTO :eof

:preserveResults
    CALL :seperator1
    ECHO Searching for connected devices after programming ...
    ECHO.
    IF EXIST !LOG_FILE! DEL !LOG_FILE!
    CALL :discover
    REM discover devices twice, because retrieving IP via DHCP needs time ...
    IF EXIST !LOG_FILE! DEL !LOG_FILE!
    CALL :discover
    IF !RC! NEQ !EXIT_SUCCESS! (
        CALL :error No device found after programming
        SET RC=!EXIT_FAULT!
    ) ELSE (
        ECHO Found devices after programming:
        CALL :printOutDevices
        ECHO.
        IF DEFINED START_WITH_NEW_REPORT (
            IF EXIST !REP_FILE! DEL !REP_FILE!
            ECHO Generate report in '!REP_FILE!' ...
        ) ELSE (
            ECHO Append report to '!REP_FILE!' ...
        )
        SET REP_DATE=
        FOR /F "usebackq" %%i IN (`date /t`) DO SET REP_DATE=%%i
        SET REP_TIME=
        FOR /F "usebackq" %%i IN (`time /t`) DO SET REP_TIME=%%i

        FOR /F "tokens=1,2,3,*" %%i IN ( !LOG_FILE! ) DO (
            SET MAC=%%i
            SET REM_IP=%%j
            SET LOC_IP=%%k
            SET VERINFO=%%l
            ECHO !REP_DATE! !REP_TIME!  !MAC!  !REM_IP!  !LOC_IP!  !VERINFO!>>!REP_FILE!
        )
    )
    GOTO :eof

:printOutDevice
    ECHO.!VERINFO!
    ECHO.   !MAC! !REM_IP! found on !LOC_IP!
    GOTO :eof

:printOutDevices
    FOR /F "tokens=1,2,3,*" %%i IN ( !LOG_FILE! ) DO (
        SET MAC=%%i
        SET REM_IP=%%j
        SET LOC_IP=%%k
        SET VERINFO=%%l
        ECHO.
        CALL :printOutDevice
    )
    GOTO :eof

:seperator1
    ECHO.
    ECHO.------------------------------------------------------------------------------
    ECHO.
    GOTO :eof

:seperator2
    ECHO.
    ECHO.+++++++++++++++++++++++++++++++++++++++++++
    ECHO.
    GOTO :eof

:usage
    ECHO.
    ECHO.NAME
    ECHO.  !SCRIPT_NAME! - download firmware file to P30/P20 devices
    ECHO.
    ECHO.USAGE
    ECHO.  !SCRIPT_NAME! [OPTIONS] [FIRMWARE BIN FILE]
    ECHO.
    ECHO.OPTIONS
    ECHO.  /?      Show this usage screen.
    ECHO.  /C:num  Specifies number of devices to program.
    ECHO.  /L      Use LoTAP flash program.
    ECHO.  /N      Generate new report file (default: append to existing).
    ECHO.  /O      Omit low-level device discovery (default: do low-level discovery)
    ECHO.  /S      Strict device discovery (same subnetwork only).
    ECHO.  /V      Be more verbose.
    ECHO.  /X      Exit without pause.
    ECHO.  /Y      Suppresses prompting to confirm you want to program a device.
    ECHO.  /-Y     Causes prompting to confirm you want to program a device.
    ECHO.
    ECHO.RETURN
    ECHO.   !EXIT_SUCCESS!      Success.
    ECHO.   !EXIT_USAGE!      Invalid usage.
    ECHO.   !EXIT_NOENT!      No such firmware file.
    ECHO.  !EXIT_FAULT!      Firmware download failed.
    ECHO.  !EXIT_NODEV!      No wallboxes available.
    ECHO.
    ECHO.REMARKS
    ECHO.  If the firmware bin file is not specified, the program tries to find
    ECHO.  a '!DEF_FW_FILE_WILD_ARGS!' file in the current working directory.
    ECHO.
    ECHO.  A P30 firmware file will be downloaded only to P30 devices:
    ECHO.  - A P30 firmware file contains the string "KEBA P30".
    ECHO.  - A P30 locator info contains the string "P30".
    ECHO.  - A P30 boot loader uses the new BOOTP/TFTP ports.
    ECHO.  *** To program a P30, an e2flash.exe of at least version 16 is necessary. ***
    ECHO.
    ECHO.  A P20 firmware file will be downloaded only to P20 devices:
    ECHO.  - A P20 firmware file contains the string "KEBA P20".
    ECHO.  - A P20 locator info contains the string "P20".
    ECHO.  - A P20 boot loader uses the original BOOTP/TFTP ports.
    ECHO.
    ECHO.  After successful download a report is appended to '!REP_FILE!'.
    GOTO :eof

:end
    IF !RC! EQU !EXIT_SUCCESS! (
        IF DEFINED DEV_FLASHED (
            CALL :preserveResults
            IF !RC! EQU !EXIT_SUCCESS! (
                CALL :seperator1
                ECHO Programming device^(s^) successfully finished.
            )
        )
    ) ELSE IF !RC! EQU !EXIT_USAGE! (
        CALL :usage
    )

    IF EXIST !LOG_FILE! DEL !LOG_FILE!
    ECHO.
    IF DEFINED PAUSE_ON_EXIT PAUSE
    IF DEFINED VERBOSE ECHO RC=!RC!
    EXIT /B !RC!

ENDLOCAL
