/** * Copyright (c) 2013-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package Moka7; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.util.Date; /** * * @author Davide Nardella * @since 1.9.0 */ public class S7Client { // WordLength private static final byte S7WLByte =0x02; private static final byte S7WLCounter =0x1C; private static final byte S7WLTimer =0x1D; // Error Codes public static final int errTCPConnectionFailed = 0x0001; public static final int errTCPDataSend = 0x0002; public static final int errTCPDataRecv = 0x0003; public static final int errTCPDataRecvTout = 0x0004; public static final int errTCPConnectionReset = 0x0005; public static final int errISOInvalidPDU = 0x0006; public static final int errISOConnectionFailed = 0x0007; public static final int errISONegotiatingPDU = 0x0008; public static final int errS7InvalidPDU = 0x0009; public static final int errS7DataRead = 0x000A; public static final int errS7DataWrite = 0x000B; public static final int errS7BufferTooSmall = 0x000C; public static final int errS7FunctionError = 0x000D; public static final int errS7InvalidParams = 0x000E; // Public fields public boolean Connected = false; public int LastError = 0; public int RecvTimeout = 2000; // Privates private static final int ISOTCP = 102; // ISOTCP Port private static final int MinPduSize = 16; private static final int DefaultPduSizeRequested = 480; private static final int IsoHSize = 7; // TPKT+COTP Header Size private static final int MaxPduSize = DefaultPduSizeRequested+IsoHSize; private Socket TCPSocket; private final byte[] PDU = new byte[2048]; private DataInputStream InStream = null; private DataOutputStream OutStream = null; private String IPAddress; private byte LocalTSAP_HI; private byte LocalTSAP_LO; private byte RemoteTSAP_HI; private byte RemoteTSAP_LO; private byte LastPDUType; private short ConnType = S7.PG; private int _PDULength = 0; // Telegrams // ISO Connection Request telegram (contains also ISO Header and COTP Header) private static final byte ISO_CR[] = { // TPKT (RFC1006 Header) (byte)0x03, // RFC 1006 ID (3) (byte)0x00, // Reserved, always 0 (byte)0x00, // High part of packet lenght (entire frame, payload and TPDU included) (byte)0x16, // Low part of packet lenght (entire frame, payload and TPDU included) // COTP (ISO 8073 Header) (byte)0x11, // PDU Size Length (byte)0xE0, // CR - Connection Request ID (byte)0x00, // Dst Reference HI (byte)0x00, // Dst Reference LO (byte)0x00, // Src Reference HI (byte)0x01, // Src Reference LO (byte)0x00, // Class + Options Flags (byte)0xC0, // PDU Max Length ID (byte)0x01, // PDU Max Length HI (byte)0x0A, // PDU Max Length LO (byte)0xC1, // Src TSAP Identifier (byte)0x02, // Src TSAP Length (2 bytes) (byte)0x01, // Src TSAP HI (will be overwritten) (byte)0x00, // Src TSAP LO (will be overwritten) (byte)0xC2, // Dst TSAP Identifier (byte)0x02, // Dst TSAP Length (2 bytes) (byte)0x01, // Dst TSAP HI (will be overwritten) (byte)0x02 // Dst TSAP LO (will be overwritten) }; // S7 PDU Negotiation Telegram (contains also ISO Header and COTP Header) private static final byte S7_PN[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x19, (byte)0x02, (byte)0xf0, (byte)0x80, // TPKT + COTP (see above for info) (byte)0x32, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00, (byte)0xf0, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e // PDU Length Requested = HI-LO 480 bytes }; // S7 Read/Write Request Header (contains also ISO Header and COTP Header) private static final byte S7_RW[] = { // 31-35 bytes (byte)0x03,(byte)0x00, (byte)0x00,(byte)0x1f, // Telegram Length (Data Size + 31 or 35) (byte)0x02,(byte)0xf0, (byte)0x80, // COTP (see above for info) (byte)0x32, // S7 Protocol ID (byte)0x01, // Job Type (byte)0x00,(byte)0x00, // Redundancy identification (byte)0x05,(byte)0x00, // PDU Reference (byte)0x00,(byte)0x0e, // Parameters Length (byte)0x00,(byte)0x00, // Data Length = Size(bytes) + 4 (byte)0x04, // Function 4 Read Var, 5 Write Var (byte)0x01, // Items count (byte)0x12, // Var spec. (byte)0x0a, // Length of remaining bytes (byte)0x10, // Syntax ID S7WLByte, // Transport Size (byte)0x00,(byte)0x00, // Num Elements (byte)0x00,(byte)0x00, // DB Number (if any, else 0) (byte)0x84, // Area Type (byte)0x00,(byte)0x00,(byte)0x00, // Area Offset // WR area (byte)0x00, // Reserved (byte)0x04, // Transport size (byte)0x00,(byte)0x00, // Data Length * 8 (if not timer or counter) }; private static final int Size_RD = 31; private static final int Size_WR = 35; // S7 Get Block Info Request Header (contains also ISO Header and COTP Header) private static final byte S7_BI[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x25, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x43, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0x09, (byte)0x00, (byte)0x08, (byte)0x30, (byte)0x41, // Block Type (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30, // ASCII Block Number (byte)0x41 }; // SZL First telegram request private static final byte S7_SZL_FIRST[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x21, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x00, // Sequence out (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x44, (byte)0x01, (byte)0x00, (byte)0xff, (byte)0x09, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, // ID (29) (byte)0x00, (byte)0x00 // Index (31) }; // SZL Next telegram request private static final byte S7_SZL_NEXT[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x21, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x08, (byte)0x12, (byte)0x44, (byte)0x01, (byte)0x01, // Sequence (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x00, (byte)0x00 }; // Get Date/Time request private static final byte S7_GET_DT[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x1d, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x47, (byte)0x01, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x00, (byte)0x00 }; // Set Date/Time command private static final byte S7_SET_DT[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x27, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x89, (byte)0x03, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x0e, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x47, (byte)0x02, (byte)0x00, (byte)0xff, (byte)0x09, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x19, // Hi part of Year (byte)0x13, // Lo part of Year (byte)0x12, // Month (byte)0x06, // Day (byte)0x17, // Hour (byte)0x37, // Min (byte)0x13, // Sec (byte)0x00, (byte)0x01 // ms + Day of week }; // S7 STOP request private static final byte S7_STOP[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x21, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x0e, (byte)0x00, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x29, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x50, (byte)0x5f, (byte)0x50, (byte)0x52, (byte)0x4f, (byte)0x47, (byte)0x52, (byte)0x41, (byte)0x4d }; // S7 HOT Start request private static final byte S7_HOT_START[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x25, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x00, (byte)0x14, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xfd, (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x50, (byte)0x5f, (byte)0x50, (byte)0x52, (byte)0x4f, (byte)0x47, (byte)0x52, (byte)0x41, (byte)0x4d }; // S7 COLD Start request private static final byte S7_COLD_START[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x27, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x0f, (byte)0x00, (byte)0x00, (byte)0x16, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xfd, (byte)0x00, (byte)0x02, (byte)0x43, (byte)0x20, (byte)0x09, (byte)0x50, (byte)0x5f, (byte)0x50, (byte)0x52, (byte)0x4f, (byte)0x47, (byte)0x52, (byte)0x41, (byte)0x4d }; // S7 Get PLC Status private static final byte S7_GET_STAT[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x21, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x2c, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x44, (byte)0x01, (byte)0x00, (byte)0xff, (byte)0x09, (byte)0x00, (byte)0x04, (byte)0x04, (byte)0x24, (byte)0x00, (byte)0x00 }; // S7 Set Session Password private static final byte S7_SET_PWD[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x25, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x27, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x45, (byte)0x01, (byte)0x00, (byte)0xff, (byte)0x09, (byte)0x00, (byte)0x08, // 8 Char Encoded Password (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }; // S7 Clear Session Password private static final byte S7_CLR_PWD[] = { (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x1d, (byte)0x02, (byte)0xf0, (byte)0x80, (byte)0x32, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x29, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x01, (byte)0x12, (byte)0x04, (byte)0x11, (byte)0x45, (byte)0x02, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x00, (byte)0x00 }; public S7Client() { // Placeholder for future implementations } public static String ErrorText(int Error) { switch (Error) { case errTCPConnectionFailed : return "TCP Connection failed."; case errTCPDataSend : return "TCP Sending error."; case errTCPDataRecv : return "TCP Receiving error."; case errTCPDataRecvTout : return "Data Receiving timeout."; case errTCPConnectionReset : return "Connection reset by the peer."; case errISOInvalidPDU : return "Invalid ISO PDU received."; case errISOConnectionFailed : return "ISO connection refused by the CPU."; case errISONegotiatingPDU : return "ISO error negotiating the PDU length."; case errS7InvalidPDU : return "Invalid S7 PDU received."; case errS7DataRead : return "S7 Error reading data from the CPU."; case errS7DataWrite : return "S7 Error writing data to the CPU."; case errS7BufferTooSmall : return "The Buffer supplied to the function is too small."; case errS7FunctionError : return "S7 function refused by the CPU."; case errS7InvalidParams : return "Invalid parameters supplied to the function."; default : return "Unknown error : 0x"+Integer.toHexString(Error); } } private int TCPConnect() { SocketAddress sockaddr = new InetSocketAddress(IPAddress, ISOTCP); LastError=0; try { TCPSocket = new Socket(); TCPSocket.connect(sockaddr ,5000); TCPSocket.setTcpNoDelay(true); InStream = new DataInputStream(TCPSocket.getInputStream()); OutStream = new DataOutputStream(TCPSocket.getOutputStream()); } catch (IOException e) { LastError=errTCPConnectionFailed; } return LastError; } private int WaitForData(int Size, int Timeout) { int cnt = 0; LastError=0; int SizeAvail; boolean Expired = false; try { SizeAvail=InStream.available(); while ((SizeAvail<Size) && (!Expired) && (LastError==0)) { cnt++; try { Thread.sleep(1); } catch (InterruptedException ex) { LastError=errTCPDataRecvTout; } SizeAvail=InStream.available(); Expired=cnt>Timeout; // If timeout we clean the buffer if (Expired && (SizeAvail>0) && (LastError==0)) InStream.read(PDU, 0, SizeAvail); } } catch (IOException ex) { LastError=errTCPDataRecvTout; } if (cnt>=Timeout) { LastError=errTCPDataRecvTout; } return LastError; } private int RecvPacket(byte[] Buffer, int Start, int Size) { int BytesRead=0; LastError=WaitForData(Size,RecvTimeout); if (LastError==0) { try { BytesRead = InStream.read(Buffer, Start, Size); } catch (IOException ex) { LastError=errTCPDataRecv; } if (BytesRead==0) LastError=errTCPConnectionReset; } return LastError; } private void SendPacket(byte[] Buffer, int Len) { LastError = 0; try { OutStream.write(Buffer,0,Len); OutStream.flush(); } catch (IOException ex) { LastError = errTCPDataSend; } } private void SendPacket(byte[] Buffer) { SendPacket(Buffer,Buffer.length); } private int RecvIsoPacket() { Boolean Done = false; int Size = 0; while ((LastError==0) && !Done) { // Get TPKT (4 bytes) RecvPacket(PDU, 0, 4); if (LastError==0) { Size=S7.GetWordAt(PDU,2); // Check 0 bytes Data Packet (only TPKT+COTP = 7 bytes) if (Size==IsoHSize) RecvPacket(PDU,4, 3); // Skip remaining 3 bytes and Done is still false else { if ((Size>MaxPduSize) || (Size<MinPduSize)) LastError=errISOInvalidPDU; else Done = true; // a valid Length !=7 && >16 && <247 } } } if (LastError==0) { RecvPacket(PDU,4, 3); // Skip remaining 3 COTP bytes LastPDUType=PDU[5]; // Stores PDU Type, we need it // Receives the S7 Payload RecvPacket(PDU, 7, Size-IsoHSize); } if (LastError==0) return Size; else return 0; } private int ISOConnect() { int Size; ISO_CR[16]=LocalTSAP_HI; ISO_CR[17]=LocalTSAP_LO; ISO_CR[20]=RemoteTSAP_HI; ISO_CR[21]=RemoteTSAP_LO; // Sends the connection request telegram SendPacket(ISO_CR); if (LastError==0) { // Gets the reply (if any) Size=RecvIsoPacket(); if (LastError==0) { if (Size==22) { if (LastPDUType!=(byte)0xD0) // 0xD0 = CC Connection confirm LastError=errISOConnectionFailed; } else LastError=errISOInvalidPDU; } } return LastError; } private int NegotiatePduLength() { int Length; // Set PDU Size Requested S7.SetWordAt(S7_PN,23,DefaultPduSizeRequested); // Sends the connection request telegram SendPacket(S7_PN); if (LastError==0) { Length=RecvIsoPacket(); if (LastError==0) { // check S7 Error if ((Length==27) && (PDU[17]==0) && (PDU[18]==0)) // 20 = size of Negotiate Answer { // Get PDU Size Negotiated _PDULength = S7.GetWordAt(PDU,25); if (_PDULength>0) return 0; else LastError=errISONegotiatingPDU; } else LastError=errISONegotiatingPDU; } } return LastError; } public void SetConnectionType(short ConnectionType) { ConnType=ConnectionType; } public int Connect() { LastError=0; if (!Connected) { TCPConnect(); if (LastError==0) // First stage : TCP Connection { ISOConnect(); if (LastError==0) // Second stage : ISOTCP (ISO 8073) Connection { LastError=NegotiatePduLength(); // Third stage : S7 PDU negotiation } } } Connected=LastError==0; // In case the connection is not completely established (TCP connection + ISO connection + PDU negotiation) // we close the socket and its IO streams to revert the object back to pre-Connect() state if (!Connected) { if (TCPSocket != null) { try { TCPSocket.close(); } catch (IOException ex) { } } if (InStream != null) { try { InStream.close(); } catch (IOException ex) { } } if (OutStream != null) { try { OutStream.close(); } catch (IOException ex) { } } _PDULength = 0; } return LastError; } public void Disconnect() { if (Connected) { try { OutStream.close(); InStream.close(); TCPSocket.close(); _PDULength=0; } catch (IOException ex) { } Connected=false; } } public int ConnectTo(String Address, int Rack, int Slot) { int RemoteTSAP=(ConnType<<8)+ (Rack * 0x20) + Slot; SetConnectionParams(Address, 0x0100, RemoteTSAP); return Connect(); } public int PDULength() { return _PDULength; } public void SetConnectionParams(String Address, int LocalTSAP, int RemoteTSAP) { int LocTSAP = LocalTSAP & 0x0000FFFF; int RemTSAP = RemoteTSAP & 0x0000FFFF; IPAddress = Address; LocalTSAP_HI = (byte) (LocTSAP>>8); LocalTSAP_LO = (byte) (LocTSAP & 0x00FF); RemoteTSAP_HI= (byte) (RemTSAP>>8); RemoteTSAP_LO= (byte) (RemTSAP & 0x00FF); } public int ReadArea(int Area, int DBNumber, int Start, int Amount, byte[] Data) { int Address; int NumElements; int MaxElements; int TotElements; int SizeRequested; int Length; int Offset = 0; int WordSize = 1; LastError=0; // If we are addressing Timers or counters the element size is 2 if ((Area==S7.S7AreaCT) || (Area==S7.S7AreaTM)) WordSize = 2; MaxElements=(_PDULength-18) / WordSize; // 18 = Reply telegram header TotElements=Amount; while ((TotElements>0) && (LastError==0)) { NumElements=TotElements; if (NumElements>MaxElements) NumElements=MaxElements; SizeRequested = NumElements * WordSize; // Setup the telegram System.arraycopy(S7_RW, 0, PDU, 0, Size_RD); // Set DB Number PDU[27] = (byte) Area; // Set Area if (Area==S7.S7AreaDB) S7.SetWordAt(PDU,25,DBNumber); // Adjusts Start and word length if ((Area==S7.S7AreaCT) || (Area==S7.S7AreaTM)) { Address = Start; if (Area==S7.S7AreaCT) PDU[22]=S7WLCounter; else PDU[22]=S7WLTimer; } else Address = Start<<3; // Num elements S7.SetWordAt(PDU,23,NumElements); // Address into the PLC (only 3 bytes) PDU[30] = (byte) (Address & 0x0FF); Address = Address >> 8; PDU[29] = (byte) (Address & 0x0FF); Address = Address >> 8; PDU[28] = (byte) (Address & 0x0FF); SendPacket(PDU, Size_RD); if (LastError==0) { Length=RecvIsoPacket(); if (LastError==0) { if (Length>=25) { if ((Length-25==SizeRequested) && (PDU[21]==(byte)0xFF)) { System.arraycopy(PDU, 25, Data, Offset, SizeRequested); Offset+=SizeRequested; } else LastError = errS7DataRead; } else LastError = errS7InvalidPDU; } } TotElements -= NumElements; Start += NumElements*WordSize; } return LastError; } public int WriteArea(int Area, int DBNumber, int Start, int Amount, byte[] Data) { int Address; int NumElements; int MaxElements; int TotElements; int DataSize; int IsoSize; int Length; int Offset = 0; int WordSize = 1; LastError=0; // If we are addressing Timers or counters the element size is 2 if ((Area==S7.S7AreaCT) || (Area==S7.S7AreaTM)) WordSize = 2; MaxElements=(_PDULength-35) / WordSize; // 18 = Reply telegram header TotElements=Amount; while ((TotElements>0) && (LastError==0)) { NumElements=TotElements; if (NumElements>MaxElements) NumElements=MaxElements; DataSize = NumElements * WordSize; IsoSize = Size_WR + DataSize; // Setup the telegram System.arraycopy(S7_RW, 0, PDU, 0, Size_WR); // Whole telegram Size S7.SetWordAt(PDU,2,IsoSize); // Data Length Length=DataSize+4; S7.SetWordAt(PDU,15,Length); // Function PDU[17]= (byte) 0x05; // Set DB Number PDU[27] = (byte) Area; if (Area==S7.S7AreaDB) S7.SetWordAt(PDU,25,DBNumber); // Adjusts Start and word length if ((Area==S7.S7AreaCT) || (Area==S7.S7AreaTM)) { Address = Start; Length = DataSize; if (Area==S7.S7AreaCT) PDU[22]=S7WLCounter; else PDU[22]=S7WLTimer; } else { Address = Start<<3; Length = DataSize<<3; } // Num elements S7.SetWordAt(PDU,23,NumElements); // Address into the PLC PDU[30] = (byte) (Address & 0x0FF); Address = Address >> 8; PDU[29] = (byte) (Address & 0x0FF); Address = Address >> 8; PDU[28] = (byte) (Address & 0x0FF); // Length S7.SetWordAt(PDU,33,Length); // Copies the Data System.arraycopy(Data, Offset, PDU, 35, DataSize); SendPacket(PDU, IsoSize); if (LastError==0) { Length=RecvIsoPacket(); if (LastError==0) { if (Length==22) { if ((S7.GetWordAt(PDU,17)!=0) || (PDU[21]!=(byte)0xFF)) LastError = errS7DataWrite; } else LastError = errS7InvalidPDU; } } Offset+=DataSize; TotElements -= NumElements; Start += NumElements*WordSize; } return LastError; } public int GetAgBlockInfo(int BlockType, int BlockNumber, S7BlockInfo Block) { int Length; LastError=0; // Block Type S7_BI[30] = (byte) BlockType; // Block Number S7_BI[31]=(byte) ((BlockNumber / 10000)+0x30); BlockNumber=BlockNumber % 10000; S7_BI[32]=(byte) ((BlockNumber / 1000)+0x30); BlockNumber=BlockNumber % 1000; S7_BI[33]=(byte) ((BlockNumber / 100)+0x30); BlockNumber=BlockNumber % 100; S7_BI[34]=(byte) ((BlockNumber / 10)+0x30); BlockNumber=BlockNumber % 10; S7_BI[35]=(byte) ((BlockNumber / 1)+0x30); SendPacket(S7_BI); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 32) // the minimum expected { if ((S7.GetWordAt(PDU,27)==0) && (PDU[29]==(byte)0xFF)) { Block.Update(PDU, 42); } else LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } /** * * @param DBNumber DB Number * @param Buffer Destination buffer * @param SizeRead How many bytes were read * @return */ public int DBGet(int DBNumber, byte[] Buffer, IntByRef SizeRead) { S7BlockInfo Block = new S7BlockInfo(); // Query the DB Length LastError = GetAgBlockInfo(S7.Block_DB, DBNumber, Block); if (LastError==0) { int SizeToRead = Block.MC7Size(); // Checks the room if (SizeToRead<=Buffer.length) { LastError=ReadArea(S7.S7AreaDB, DBNumber, 0, SizeToRead, Buffer); if (LastError==0) SizeRead.Value=SizeToRead; } else LastError=errS7BufferTooSmall; } return LastError; } public int ReadSZL(int ID, int Index, S7Szl SZL) { int Length; int DataSZL; int Offset = 0; boolean Done = false; boolean First = true; byte Seq_in =0x00; int Seq_out =0x0000; LastError=0; SZL.DataSize=0; do { if (First) { S7.SetWordAt(S7_SZL_FIRST, 11, ++Seq_out); S7.SetWordAt(S7_SZL_FIRST, 29, ID); S7.SetWordAt(S7_SZL_FIRST, 31, Index); SendPacket(S7_SZL_FIRST); } else { S7.SetWordAt(S7_SZL_NEXT, 11, ++Seq_out); PDU[24] = (byte)Seq_in; SendPacket(S7_SZL_NEXT); } if (LastError!=0) return LastError; Length=RecvIsoPacket(); if (LastError==0) { if (First) { if (Length > 32) // the minimum expected { if ((S7.GetWordAt(PDU,27)==0) && (PDU[29]==(byte)0xFF)) { // Gets Amount of this slice DataSZL=S7.GetWordAt(PDU,31)-8; // Skips extra params (ID, Index ...) Done=PDU[26]==0x00; Seq_in=(byte)PDU[24]; // Slice sequence SZL.LENTHDR=S7.GetWordAt(PDU, 37); SZL.N_DR=S7.GetWordAt(PDU, 39); SZL.Copy(PDU, 41, Offset, DataSZL); Offset+=DataSZL; SZL.DataSize+=DataSZL; } else LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } else { if (Length > 32) // the minimum expected { if ((S7.GetWordAt(PDU,27)==0) && (PDU[29]==(byte)0xFF)) { // Gets Amount of this slice DataSZL=S7.GetWordAt(PDU,31); Done=PDU[26]==0x00; Seq_in=(byte)PDU[24]; // Slice sequence SZL.Copy(PDU, 37, Offset, DataSZL); Offset+=DataSZL; SZL.DataSize+=DataSZL; } else LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } } First=false; } while(!Done && (LastError==0)); return LastError; } public int GetCpuInfo(S7CpuInfo Info) { S7Szl SZL = new S7Szl(1024); LastError = ReadSZL(0x001C, 0x0000, SZL); if (LastError==0) { Info.Update(SZL.Data, 0); } return LastError; } public int GetCpInfo(S7CpInfo Info) { S7Szl SZL = new S7Szl(1024); LastError = ReadSZL(0x0131, 0x0001, SZL); if (LastError==0) { Info.Update(SZL.Data, 0); } return LastError; } public int GetOrderCode(S7OrderCode Code) { S7Szl SZL = new S7Szl(1024); LastError = ReadSZL(0x0011, 0x0000, SZL); if (LastError==0) { Code.Update(SZL.Data, 0, SZL.DataSize); } return LastError; } public int GetPlcDateTime(Date DateTime) { int Length; LastError = 0; SendPacket(S7_GET_DT); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 30) // the minimum expected { if ((S7.GetWordAt(PDU,27)==0) && (PDU[29]==(byte)0xFF)) { DateTime=S7.GetDateAt(PDU, 34); } else LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int SetPlcDateTime(Date DateTime) { int Length; LastError = 0; S7.SetDateAt(S7_SET_DT, 31, DateTime); SendPacket(S7_SET_DT); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 30) // the minimum expected { if (S7.GetWordAt(PDU,27)!=0) LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int SetPlcSystemDateTime() { return SetPlcDateTime(new Date()); } public int PlcStop() { int Length; LastError = 0; SendPacket(S7_STOP); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 18) // 18 is the minimum expected { if (S7.GetWordAt(PDU,17)!=0) LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int PlcHotStart() { int Length; LastError = 0; SendPacket(S7_HOT_START); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 18) // the minimum expected { if (S7.GetWordAt(PDU,17)!=0) LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int PlcColdStart() { int Length; LastError = 0; SendPacket(S7_COLD_START); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 18) // the minimum expected { if (S7.GetWordAt(PDU,17)!=0) LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int GetPlcStatus(IntByRef Status) { int Length; LastError = 0; SendPacket(S7_GET_STAT); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 30) // the minimum expected { if (S7.GetWordAt(PDU,27)==0) { switch (PDU[44]) { case S7.S7CpuStatusUnknown : case S7.S7CpuStatusRun : case S7.S7CpuStatusStop : Status.Value=PDU[44]; break; default : // Since RUN status is always 0x08 for all CPUs and CPs, STOP status // sometime can be coded as 0x03 (especially for old cpu...) Status.Value=S7.S7CpuStatusStop; } } else LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int SetSessionPassword(String Password) { byte[] pwd = {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}; int Length; LastError = 0; // Adjusts the Password length to 8 if (Password.length()>8) Password=Password.substring(0, 8); else { while (Password.length()<8) Password=Password+" "; } try { pwd = Password.getBytes("UTF-8"); } catch (UnsupportedEncodingException ex) { LastError = errS7InvalidParams; } if (LastError==0) { // Encodes the password pwd[0]=(byte) (pwd[0] ^ 0x55); pwd[1]=(byte) (pwd[1] ^ 0x55); for (int c = 2; c < 8; c++) { pwd[c]=(byte) (pwd[c] ^ 0x55 ^ pwd[c-2]); } System.arraycopy(pwd, 0, S7_SET_PWD, 29, 8); // Sends the telegrem SendPacket(S7_SET_PWD); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 32) // the minimum expected { if (S7.GetWordAt(PDU,27)!=0) LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } } return LastError; } public int ClearSessionPassword() { int Length; LastError = 0; SendPacket(S7_CLR_PWD); if (LastError==0) { Length=RecvIsoPacket(); if (Length > 30) // the minimum expected { if (S7.GetWordAt(PDU,27)!=0) LastError = errS7FunctionError; } else LastError = errS7InvalidPDU; } return LastError; } public int GetProtection(S7Protection Protection) { S7Szl SZL = new S7Szl(256); LastError = ReadSZL(0x0232, 0x0004, SZL); if (LastError==0) { Protection.Update(SZL.Data); } return LastError; } }