Lean MES Open Source Manufacturing Execution System
Flexible Manufacturing Solutions through Open Standards and Free Software....

Banner Presence Plus P4/COLOR Communication Driver for OPTO22 PAC


Compatible with Banner  PresencePLUS P4 and P4 COLOR Series products.
Stable active production confirmed on: SNAP-PAC-R2, S1 (Rev.8.4a); LCE, ADS, M64 (Rev.7.2h).


Deployment/Test Instructions:
1. Sensor IP and PAC IP are configured ,preferably, on the same subnet. Both devices connected to the same physical network. (Removes unnecessary routing and reduce risk of failure).
2. Verify and update sensor configuration if necessary with the product ID 99 (default). It has to fail regardless.
2. Only power and Ethernet connection are required. No digital inputs/pulses used at all.
3. Sensor has to have at least 1 functional product ID configured, e.g. product ID 1.
4. Download the ctEth_BannerOmniPpP4_VSxx.zip and extract files.
5. Copy and include files from extracted .\subroutines folder to strategy's subroutines folder
6. Import the extracted ctEth_BannerOmniPpP4_VSxx.cxf into strategy
   (Note! all tags are going to maintain their generic template names,
    for replication and reuse technique in PAC Control click here coming soon...).
7. Update chTTxx_tcp tag in PAC Control Communication Handles to a specific tool's IP address, keep port 502.
8. Compile strategy, load to PAC and click the Run Strategy button.
9. Run the driver flow(chart). Verify nVSxx_CommSts has value 1.
10. See example of communication command sequences here (coming soon...).
11. See example of handling vision sensor instruction(s) in OI sample here.



//***********************************************************************************
//Banner Presence+ P4/COLOR Communication Driver for OPTO22 PACs
//Driver Chart
//Copyright (C) 2010  Peter Tiagunov peter.tiagunov@leanmes.net

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

//Modbus Function Codes Supported
//01: Read Coil Status
//02: Read Input Status
//03: Read Holding Registers
//04: Read Input Register(s)    - used in this implementation
//05: Force Single Coil
//06: Preset Single Register    - used in this implementation
//07: Read Exception Status
//15: Write Multiple Coils
//16: Preset Multiple Registers

//Output Coils (10000-10032) - 32 bits
//Output Register 1 (reference 0 / 10000-10015)
//--------------------------------------------------------------------------------------------------------
//Bit#   |15   |14   |13   |12   |11   |10   |9    |8    |7    |6    |5    |4    |3    |2    |1    |0    |
//--------------------------------------------------------------------------------------------------------
//Name   |rsrvd|rsrvd|I/O#6|I/O#5|I/O#4|I/O#3|I/O#2|I/O#1|rsrvd|rsrvd|rsrvd|Ready|rsrvd|Error|Fail |Pass |
//--------------------------------------------------------------------------------------------------------
//Output Register 2 (reference 1 / 10016-10032)
//--------------------------------------------------------------------------------------------------------
//Bit#   |15   |14   |13   |12   |11   |10   |9    |8    |7    |6    |5    |4    |3    |2    |1    |0    |
//--------------------------------------------------------------------------------------------------------
//Name   |rsrvd|rsrvd|rsrvd|rsrvd|rsrvd|rsrvd|rsrvd|BCRSt|rsrvd|rsrvd|rsrvd|rsrvd|rsrvd|Prdct|RmTch|Trigg|
//Name   |     |     |     |     |     |     |     |ChAck|     |     |     |     |     |ChAck|Ack  |Ack  |
//--------------------------------------------------------------------------------------------------------


//-----------------------------------------------------------------------------------------------------------------
nVSxx_CommReady=0;
if(srModbusTCP(chVSxx_ModbusTCP,     //  > in Vision Sensor communication handle
               nVSxx_TransactionId,  //  <>io ModbusTCP transaction Identifier (0-65535 or 00 00 - FF FF)
               nVSxx_Function,       //  > in ModbusTCP Function code (see list of Modbus Function Codes Supported)
               nVSxx_RefNr,          //  <>in ModbusTCP Reference Number (Start Address)
               nVSxx_Data,           //  > in 16-bit (Single Register Value, Function 06 or Word Count, Function 04)
               ntVSxx_ORegister,     //  < out Sensor Output Register 1 (status)
               nVSxx_CommSts,        //  < out Communication Status
               nVSxx_Error           //  < out error code
              )==0)then
endif
//-----------------------------------------------------------------------------------
if (nVSxx_CommSts and ntVSxx_ORegister[0]<>-1 and ntVSxx_ORegister[1]<>-1)then //if not powerup
    nVSxx_ORegister=ntVSxx_ORegister[0];
    //get Sensor Error status
    nVSxx_StsError=BitTest(nVSxx_ORegister,2);
    if(nVSxx_StsError)then //sensor returned error
        nVSxx_Error=7826;//Vision Sensor - Status Error, Cycle Vision Sensor Power
    endif
    //get Sensor Ready status
    nVSxx_StsReady=BitTest(nVSxx_ORegister,4);
    if(not(nVSxx_StsReady))then //sensor not ready
    endif
    //get Pass/Fail status
    nVSxx_StsFail    =BitTest(nVSxx_ORegister,1); //fail status
    nVSxx_StsPass    =BitTest(nVSxx_ORegister,0); //pass status

    nVSxx_ORegister=ntVSxx_ORegister[1];
    //get Product Change ACK and Trigger ACK
    nVSxx_InspSet_Ack=BitTest(nVSxx_ORegister,2);
    nVSxx_InspTrg_Ack=BitTest(nVSxx_ORegister,0);
else //override status
    nVSxx_CommSts  =0;
    nVSxx_StsReady =0;
    nVSxx_StsError =0;
endif
nVSxx_CommReady=1;
//----------------------------------------------------------------------------------

//--control scan/reconnect rate--------------------------
if(nVSxx_CommSts==0)then
    nVSxx_ScanRate=5000;
        //----------------------------------------------------------
        //read 8 Output Registers (status registers)
        nVSxx_RefNr    =0; //set Reference Number (Start Address)
        nVSxx_Data     =8; //set number of word(s) to read
        nVSxx_Function =4; //set function Read Input Register(s)
        //----------------------------------------------------------
elseif(nVSxx_CommSts==1 and nVSxx_ScanRate==5000)then
    nVSxx_ScanRate=10;
endif

//--keepalive/read status timer--------------------------
if(nVSxx_CommSts==1)then
  if(nVSxx_Function==0)then
    if (HasDownTimerExpired(tdVSxx_KeepAlive)) then
        //----------------------------------------------------------
        //read 2 Output Registers (status registers)
        nVSxx_Function =4; //set function Read Input Register(s)
        nVSxx_RefNr    =0; //set Reference Number (Start Address)
        nVSxx_Data     =8; //set number of word(s) to read
        //----------------------------------------------------------
        SetDownTimerPreset(fVSxx_KeepAliveTimer,tdVSxx_KeepAlive);
        StartTimer(tdVSxx_KeepAlive);
    endif
  else //restart timer if communication present
    SetDownTimerPreset(fVSxx_KeepAliveTimer,tdVSxx_KeepAlive);
    StartTimer(tdVSxx_KeepAlive);
  endif
endif

DelayMsec(nVSxx_ScanRate);




//***********************************************************************************
//Banner Presence+ P4/COLOR Communication Driver for OPTO22 PACs
//Subroutine srModbusTCP.Binary Converter
//Copyright (C) 2010  Peter Tiagunov peter.tiagunov@leanmes.net

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

//convert to binary
sTxBuff=""; //clear Tx buffer
if(in_Function>0)then //begin new transaction
    //-------------------------------------------
        if(io_TransactionId<65535)then //check transaction id rollover
            io_TransactionId=io_TransactionId+1;
        else
            io_TransactionId=0;
        endif
        //-- convert Transaction Id (16-bits or 2 bytes) ------------------------------------------------------
        NumberToHexString(io_TransactionId,s4Transaction);//get hex value of address integer (example: 10 -> A)
        //s4Transaction string length = 4 x 4 bit characters
        //left pad sAddress with "0"'s (16-bit format), example: 1 > 0001 or 800 > 0800 or F0A > 0F0A...etc.
        if(GetStringLength(s4Transaction)<4)then
            while(GetStringLength(s4Transaction)<4)
                s4Transaction="0"+s4Transaction;
            wend
        endif
        //extract HI byte from a 16-bit value of sAddress, example: 08F0 - 08
        GetSubstring(s4Transaction, 0, 2, s2ByteHI); //high byte
        //extract LO byte from a 16-bit value of sAddress, example: 08F0 - F0
        GetSubstring(s4Transaction, 2, 2, s2ByteLO); //low byte
        s4Transaction=Chr(HexStringToNumber(s2ByteHI))+Chr(HexStringToNumber(s2ByteLO));
        //-----------------------------------------------------------------------------------------------------

        //set Protocol Identifier (constant= 00 00)
        s4ProtocolId=Chr(0)+Chr(0);

        //set Length of Modbus Frame, constant = 00 06 (6 bytes)
        s4ModbusLength=Chr(0)+Chr(6);  
 
        //set  Unit Identifier, constant= 00
        s2UnitId=Chr(0);

        //-- convert Function, example: 06 - Write Single Register ------------------------------------
        NumberToHexString(in_Function,s2Function);//get hex value of address integer (example: 10 -> A)
        //left pad sAddress with "0"'s (8-bit format), example: 1 > 01 or 8 > 08 or A > 0A...etc.
        if(GetStringLength(s2Function)<2)then
            while(GetStringLength(s2Function)<2)
                s2Function="0"+s2Function;
            wend
        endif
        GetSubstring(s2Function, 0, 2, s2ByteHI); //high byte
        s2Function=Chr(HexStringToNumber(s2ByteHI));
        //---------------------------------------------------------------------------------------------

        //-- convert Reference Nr. (Register Address) (16-bits or 2 bytes) ---------------------------------
        NumberToHexString(in_RefNr,s4RefNr);//get hex value of address integer (example: 10 -> A)
        //s4Transaction string length = 4 x 4 bit characters
        //left pad sAddress with "0"'s (16-bit format), example: 1 > 0001 or 800 > 0800 or F0A > 0F0A...etc.
        if(GetStringLength(s4RefNr)<4)then
            while(GetStringLength(s4RefNr)<4)
                s4RefNr="0"+s4RefNr;
            wend
        endif
        //set Reference Nr.
        //extract HI byte from a 16-bit value of sAddress, example: 08F0 - 08
        GetSubstring(s4RefNr, 0, 2, s2ByteHI); //high byte
        //extract LO byte from a 16-bit value of sAddress, example: 08F0 - F0
        GetSubstring(s4RefNr, 2, 2, s2ByteLO); //low byte
        s4RefNr=Chr(HexStringToNumber(s2ByteHI))+Chr(HexStringToNumber(s2ByteLO));
        //--------------------------------------------------------------------------------------------------

        //-- convert Data/Word Count (16-bits or 2 bytes) -------------------------------------------------
        NumberToHexString(in_Data,s4Data);//get hex value of address integer (example: 10 -> A)
        //s4Transaction string length = 4 x 4 bit characters
        //left pad sAddress with "0"'s (16-bit format), example: 1 > 0001 or 800 > 0800 or F0A > 0F0A...etc.
        if(GetStringLength(s4Data)<4)then
            while(GetStringLength(s4Data)<4)
                s4Data="0"+s4Data;
            wend
        endif
        //set Data
        //extract HI byte from a 16-bit value of Data/Word, example: 08F0 - 08
        GetSubstring(s4Data, 0, 2, s2ByteHI); //high byte
        //extract LO byte from a 16-bit value of Data/Word, example: 08F0 - F0
        GetSubstring(s4Data, 2, 2, s2ByteLO); //low byte
        s4Data=Chr(HexStringToNumber(s2ByteHI))+Chr(HexStringToNumber(s2ByteLO));
        //-------------------------------------------------------------------------------------------------
    sTxBuff=s4Transaction+s4ProtocolId+s4ModbusLength+s2UnitId+s2Function+s4RefNr+s4Data;
endif




//***********************************************************************************
//Banner Presence+ P4/COLOR Communication Driver for OPTO22 PACs
//Subroutine srModbusTCP.Modbus TCP Socket
//Copyright (C) 2010  Peter Tiagunov peter.tiagunov@leanmes.net

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

if(not(IsCommunicationOpen(in_ch)))then //try to establish communication
    n32CommSts=OpenOutgoingCommunication(in_ch);
    if(n32CommSts==0 or n32CommSts==-47)then //socket created
        n32CommSts=SendCommunicationHandleCommand(in_ch, "set.to:1.0");
        ClearCommunicationReceiveBuffer(in_ch);
        out_error=0;
        io_TransactionId=0;//initialize transaction identifier
        out_commsts=1;
    else
        //clear the registers table
        i=0;
        while(i<GetLengthOfTable(out_Registers))
            out_Registers[i]=-1;
            i=i+1;
        wend
        out_error=7800;//create socket error
    endif
else //communication established
        //send command
        if(GetStringLength(sTxBuff)>0)then
            ClearCommunicationReceiveBuffer(in_ch);
            n32CommSts=TransmitString(sTxBuff,in_ch);
            if(n32CommSts==0)then //data transmisson success
                n32Timeout=1000;
                //wait for reply
                sRxBuff="";
                while(sRxBuff=="" and n32Timeout>0)
                    DelayMsec(5);
                    n32CommSts=GetNumCharsWaiting(in_ch);
                    if(n32CommSts>=6)then //receive data available
                        n32CommSts=ReceiveNChars(sRxBuff, n32CommSts, in_ch);
                        if(n32CommSts==0)then //receive success
                            //--------------------------------------------------------------------------------
                            if(in_Function==4)then //if Read Input Register(s)
                                //get substring cosists of 4-th and 5-th character, starting from 0
                                GetSubstring(sRxBuff,4,2,s2Length); //get length in bytes of received Modbus frame

                                //convert string hex to integer
                                i=GetStringLength(s2Length)-1;
                                nLength=0;
                                while(i>=0 and s2Length[i]>0)
                                    if(i>0)then
                                        nLength=(nLength * 256) + s2Length[i];
                                    else //compensate for signed 32-bit integer
                                        nLength=(nLength-1) * 128 + s2Length[i];
                                    endif
                                    i=i-1;
                                wend
                                    //Header           |Modbus Frame        |
                                    //              +--+--+--+--+-----+-----+
                                    //              |ln|ui|fn|bc|Data0|Data1| -one or more 16-bit word(s) (2 bytes each)
                                    //--------------+--+--+--+--+-----+-----+
                                    // 0  1  2  3  4| 5| 6| 7| 8| 9 10|11 12| -string character index
                                    //--------------+--+--+--+--+-----+-----+
                                    //00 01 00 00 00|07|00|04|04|30 10|00 01|
                                //get the Modbus Frame data
                                GetSubstring(sRxBuff,6,(GetStringLength(sRxBuff)-6),sModbusFrame);

                                if(nLength==GetStringLength(sModbusFrame))then //the length of Modbus frame match

                                    //begin to parse the data, get byte count
                                    nByteCount=sModbusFrame[2];
                                    //note, byte count is multiplier by 2 (2 bytes = 16-bits), max nr of bytes is 32 or 16 words
                                    if(nByteCount>0)then
                                        GetSubstring(sModbusFrame,3,nByteCount,s32);
                                        //check number of bytes received, each byte is one character
                                        if(nByteCount==GetStringLength(s32))then
                                            //clear registers table
                                            i=0;
                                            while(i<GetLengthOfTable(out_Registers))
                                                out_Registers[i]=-1;
                                                i=i+1;
                                            wend
                                            i=0;
                                            j=0;
                                            while(j<nByteCount-1)
                                                out_Registers[i]=s32[j]; //get MSB
                                                out_Registers[i]=out_Registers[i] << 8;
                                                out_Registers[i]=out_Registers[i] bitor s32[j+1];//get LSB
                                                i=i+1;
                                                j=j+2;
                                            wend
                                            //clear the rest of registers table
                                            while(i<GetLengthOfTable(out_Registers))
                                                out_Registers[i]=-1;
                                                i=i+1;
                                            wend
                                                in_Function=0;//complete transaction
                                                out_error=0;//clear error
                                        else
                                            out_error=7824;//ModbusTCP - Number of Byte(s) Received is Incorrect
                                        endif
                                    else
                                        out_error=7823;//ModbusTCP - Byte(s) not Received
                                    endif
                                else
                                    out_error=7822;//ModbusTCP - The Length of Modbus Frame is Invalid
                                endif

                            elseif(in_Function==6)then//if Preset Single Register, verify response received
                                if(sTxBuff==sRxBuff)then
                                    in_Function=0;//complete transaction
                                    out_error=0;//clear error
                                else
                                    out_error=7821;//ModbusTCP - Write Function no Ack.
                                endif
                            else
                                out_error=7820;//ModbusTCP - Unsupported Function
                            endif
                            //--------------------------------------------------------------------------------
                        else
                            out_error=7805;//socket receive error
                        endif
                    elseif(n32CommSts<0)then //no data
                        out_error=7804;//socket receive buffer error
                    endif
                      n32Timeout=n32Timeout-5;
                      //exit while on timeout or error
                    if(n32Timeout<=0)then
                        out_error=7803;//communication reply timeout
                    elseif(out_error>0)then
                        n32Timeout=0;//exit while
                    endif
                wend
            else
                out_error=7802;//socket send error
            endif
        endif
    //-------------------------------------------
endif

if(out_error>0)then
    if(CloseCommunication(in_ch)<>0)then
        out_error=7809; //close socket error
    endif
    out_commsts=0;
else
    out_commsts=1;
endif