unit TCPStorage; // ****************************************************************************** // // * Copyright: // (c) Mustangpeak Software 2012. // // The contents of this file are subject to the GNU GPL v3 licence/ you maynot use // this file except in compliance with the License. You may obtain a copy of the // License at http://www.gnu.org/licenses/gpl.html // // * Revision History: // 2012-04-01: 1.0.0.0 Created // 2012-10-07: Version 1.0 // // * Description: // Implements a FIFO data structure for CAN Message Buffers // // ****************************************************************************** {$I Options.inc} {.$DEFINE LOG_UART} {$DEFINE LOG_UART1} {.$DEFINE LOG_GRIDCONNECT_UART} uses HelperFunctions, NMRAnetDefinesShared, NMRAnetDefines; const CAN_DIRECTION_READ = 1; CAN_DIRECTION_WRITE = 0; OLCB_SERVER_IP = '192.168.0.127'; OLCB_SERVER_PORT = 12021; const CES_TRANSMITTING = $0001; // CAN Engine State constants const BS_CLEAR = $00; BS_EXTENDED = $01; // CAN Buffer State constants, Buffer is extended BS_ALLOCATED = $02; // Buffer Slot contains valid data to send {const SOCKET_STATE_CLOSED = 0; SOCKET_STATE_SYN_REC_SYN_SENT_WAIT_ACK = 1; SOCKET_STATE_ESTABLISHED = 3; SOCKET_STATE_SYN_SENT_WAIT_FOR_SYN_RESPONSE = 4; SOCKET_STATE_FIN_SENT_WAIT_FOR_ACK = 5; SOCKET_STATE_FIN_ACK_REC_WAIT_FOR_REMOTE_FIN = 6; SOCKET_STATE_EXPIRED_ACK_WAIT_TIME = 7; } type // *************************************************************************** // CAN Message Buffers in user friendly form that can be easily maniuplated then // loaded into the Raw CAN FIFOs // *************************************************************************** TCANBuffer = record ID: DWord; DataCount: Byte; DataBytes: TCAN_DataBytes; State: Byte; // See CBS_xxxx Constants (CAN Buffer State) end; PCANBuffer = ^TCANBuffer; const MAX_NMRANET_BUFFER_SIZE = 30; type TCAN_Engine = record State: Byte; // See the CES_xxx constants (CAN Engine State) InterruptLockCount: Byte; TX_CANBuffer, TX_AddressedErrorBuffer, TX_DatagramRejected: TCANBuffer; TX_NMRAnetBuffer: array[MAX_NMRANET_BUFFER_SIZE] of TCANBuffer; TX_NMRAnetBufferHead, TX_NMRAnetBufferTail: Integer; TX_NMRAnetBufferFull: Boolean; TX_NMRAnetBufferSent, TX_NMRAnetBufferLoaded: DWord; TransmitImmediately: Boolean; IsTransmitting: Boolean; end; procedure CANStorage_Initialize; procedure CANStorage_FlushBuffers(AliasID: Word); procedure Do_Storage_Send; procedure LockCANInterrupt; procedure UnLockCANInterrupt; procedure StartCANMessageEngine; // Used to start a transmission cycle after a message is placed in a TX Buffer procedure StartCANHighPriorityMessageEngine; function CANStorage_NextToSend: PCANBuffer; function CANStorage_NextHighPriorityToSend: PCANBuffer; function CANStorage_NMRAnetBufferAvailable: Boolean; procedure CANStorage_NMRAnetBufferStore(CANBuffer: PCANBuffer); function CANStorage_LowPriorityBufferReady: Boolean; function CANStorage_HighPriorityBufferReady: Boolean; // Exported for the TCP Stack procedure DisableInt; procedure EnableInt; procedure Eth_UserTCP(var dest_ip_addr_T : TIPAddress; var source_port_T, dest_port_T, len_T : word; iSocket : Integer); function Eth_UserUDP(var dest_ip_addr_U : TIPAddress; var dest_port_U, source_port_U, len_U : word) : word; //procedure CANStorage_DumpSocketData(Socket: ^SOCKET_28j60_Dsc); procedure ReceivedOnFilter0(CANBuffer: PCANBuffer); external; // CAN Layer Message procedure ReceivedOnFilter1(CANBuffer: PCANBuffer); external; // NMRAnet Layer Message procedure CANBufferToGridConnect(Buffer: PCANBuffer; var GridConnectBuffer: TGridConnectString); external; procedure GridConnectToCANBuffer(var GridConnectBuffer: TGridConnectString; Buffer: PCANBuffer); external; var // OlcbSocket: ^SOCKET_28j60_Dsc; CAN_Engine: TCAN_Engine; implementation const TCP_STATE_SYNC_START = 0; TCP_STATE_SYNC_FIND_X = 1; TCP_STATE_SYNC_FIND_HEADER = 2; TCP_STATE_SYNC_FIND_N = 3; TCP_STATE_SYNC_FIND_DATA = 4; var TCP_Receive_State: word; // Statemachine Index procedure CANStorage_Initialize; var i: Integer; begin // OlcbSocket := nil; TCP_Receive_State := TCP_STATE_SYNC_START; CAN_Engine.State := 0; CAN_Engine.InterruptLockCount := 0; CAN_Engine.TX_CANBuffer.ID := 0; CAN_Engine.TX_CANBuffer.DataCount := 0; CAN_Engine.TX_CANBuffer.State := 0; for i := 0 to MAX_NMRANET_BUFFER_SIZE -1 do begin CAN_Engine.TX_NMRAnetBuffer[i].ID := 0; CAN_Engine.TX_NMRAnetBuffer[i].DataCount := 0; CAN_Engine.TX_NMRAnetBuffer[i].State := 0; end; CAN_Engine.TX_AddressedErrorBuffer.ID := 0; CAN_Engine.TX_AddressedErrorBuffer.DataCount := 0; CAN_Engine.TX_AddressedErrorBuffer.State := 0; CAN_Engine.TX_DatagramRejected.ID := 0; CAN_Engine.TX_DatagramRejected.DataCount := 0; CAN_Engine.TX_DatagramRejected.State := 0; CAN_Engine.TX_NMRAnetBufferHead := 0; CAN_Engine.TX_NMRAnetBufferTail := 0; CAN_Engine.TX_NMRAnetBufferFull := False; CAN_Engine.TX_NMRAnetBufferLoaded := 0; CAN_Engine.TX_NMRAnetBufferSent := 0; CAN_Engine.TransmitImmediately := False; CAN_Engine.IsTransmitting := False; end; { procedure CANStorage_DumpSocketData(Socket: ^SOCKET_28j60_Dsc); begin UART1_Write_Text('remoteIP: ' + IPAddressToStr(Socket^.remoteIP) + LF); UART1_Write_Text('remoteMAC: ' + IPAddressToStr(Socket^.remoteMAC) + LF); WordToStr(Socket^.remotePort, s1); UART1_Write_Text('remote Port ' + s1 + LF); WordToStr(Socket^.destPort, s1); UART1_Write_Text('destination Port ' + s1 + LF); WordToStr(Socket^.dataLength, s1); UART1_Write_Text('TCP payload size (refers to the last received package) ' + s1 + LF); WordToStr(Socket^.RemoteMSS, s1); UART1_Write_Text('Remote Max Segment Size ' + s1 + LF); WordToStr(Socket^.myWin, s1); UART1_Write_Text('My Windows ' + s1 + LF); WordToStr(Socket^.myMSS, s1); UART1_Write_Text('My Max Segement Size ' + s1 + LF); LongWordToStr(Socket^.MySeq, s1); UART1_Write_Text('My Current Sequence ' + s1 + LF); LongWordToStr(Socket^.MyACK, s1); UART1_Write_Text('ACK ' + s1 + LF); ByteToStr(Socket^.stateTimer, s1); UART1_Write_Text('State Timer ' + s1 + LF); ByteToStr(Socket^.retransmitTimer, s1); UART1_Write_Text('Retransmit Timer ' + s1 + LF); WordToStr(Socket^.packetID, s1); UART1_Write_Text('Packet ID ' + s1 + LF); ByteToStr(Socket^.open, s1); UART1_Write_Text('=0 -> Socket busy; =1 -> Socket free ' + s1 + LF); ByteToStr(Socket^.ID, s1); UART1_Write_Text('Socket ID ' + s1 + LF); ByteToStr(Socket^.broadcastMark, s1); UART1_Write_Text('0 -> Not broadcast; =1 -> Broadcast ' + s1 + LF); ByteToStr(Socket^.state, s1); UART1_Write_Text('State ' + s1 + LF); WordToStr(Socket^.nextSend, s1); UART1_Write_Text('"Pointer" on first byte in buffer we want to send ' + s1 + LF); WordToStr(Socket^.lastACK, s1); UART1_Write_Text('"Pointer" on last acknowledged byte in buffer ' + s1 + LF); WordToStr(Socket^.lastSent, s1); UART1_Write_Text('Pointer" on last sent byte in buffer ' + s1 + LF); WordToStr(Socket^.lastWritten, s1); UART1_Write_Text('"Pointer" on last written byte in buffer which not sent yet ' + s1 + LF); WordToStr(Socket^.numToSend, s1); UART1_Write_Text('Number of bytes in buffer to be sent ' + s1 + LF); WordToStr(Socket^.buffState, s1); UART1_Write_Text('Private variable ' + s1 + LF); ByteToHex(Socket^.txBuffer, s1); UART1_Write_Text('Pointer on Tx Buffer 0x' + s1 + LF); end; } procedure PackNMRAnetBuffer; var i: Integer; begin // remove any state = 0 buffer and readjust head/tail pointer end; procedure CANStorage_FlushBuffers(AliasID: Word); var i: Integer; begin LockCANInterrupt; if AliasID = 0 then begin CAN_Engine.TX_CANBuffer.State := 0; for i := 0 to MAX_NMRANET_BUFFER_SIZE - 1 do CAN_Engine.TX_NMRAnetBuffer[i].State := 0; CAN_Engine.TX_AddressedErrorBuffer.State := 0; CAN_Engine.TX_DatagramRejected.State := 0; CAN_Engine.TX_NMRAnetBufferHead := 0; CAN_Engine.TX_NMRAnetBufferTail := 0; CAN_Engine.TX_NMRAnetBufferFull := False; end else begin if CAN_Engine.TX_CANBuffer.ID and MASK_SOURCE_ALIAS = AliasID then CAN_Engine.TX_CANBuffer.State := 0; for i := 0 to MAX_NMRANET_BUFFER_SIZE - 1 do begin if CAN_Engine.TX_NMRAnetBuffer[i].ID and MASK_SOURCE_ALIAS = AliasID then CAN_Engine.TX_NMRAnetBuffer[i].State := 0; end; if CAN_Engine.TX_AddressedErrorBuffer.ID and MASK_SOURCE_ALIAS = AliasID then CAN_Engine.TX_AddressedErrorBuffer.State := 0; if CAN_Engine.TX_DatagramRejected.ID and MASK_SOURCE_ALIAS = AliasID then CAN_Engine.TX_DatagramRejected.State := 0; PackNMRAnetBuffer; end; UnLockCANInterrupt end; function CANStorage_NextToSend: PCANBuffer; // the caller of the MUST USE THE BUFFER and clear the state flag immediatly before allowing any new // items to be placed in the buffer!!!! begin // High Priority to Low Result := PCANBuffer( nil); if CANStorage_LowPriorityBufferReady then begin if CAN_Engine.TX_CANBuffer.State and BS_ALLOCATED = BS_ALLOCATED then Result := @CAN_Engine.TX_CANBuffer else begin Result := @CAN_Engine.TX_NMRAnetBuffer[CAN_Engine.TX_NMRAnetBufferHead]; Inc(CAN_Engine.TX_NMRAnetBufferHead); if CAN_Engine.TX_NMRAnetBufferHead >= MAX_NMRANET_BUFFER_SIZE then CAN_Engine.TX_NMRAnetBufferHead := 0; CAN_Engine.TX_NMRAnetBufferFull := False; // By definition if we cleared one it can't be full! end end end; function CANStorage_NextHighPriorityToSend: PCANBuffer; begin if CAN_Engine.TX_AddressedErrorBuffer.State and BS_ALLOCATED = BS_ALLOCATED then Result := @CAN_Engine.TX_AddressedErrorBuffer else if CAN_Engine.TX_DatagramRejected.State and BS_ALLOCATED = BS_ALLOCATED then Result := @CAN_Engine.TX_DatagramRejected else Result := PCANBuffer( nil); end; function CANStorage_NMRAnetBufferAvailable: Boolean; begin Result := not CAN_Engine.TX_NMRAnetBufferFull end; function CANStorage_LowPriorityBufferReady: Boolean; begin Result := (CAN_Engine.TX_NMRAnetBufferHead <> CAN_Engine.TX_NMRAnetBufferTail) or CAN_Engine.TX_NMRAnetBufferFull or (CAN_Engine.TX_CANBuffer.State and BS_ALLOCATED = BS_ALLOCATED) end; function CANStorage_HighPriorityBufferReady: Boolean; begin Result := (CAN_Engine.TX_AddressedErrorBuffer.State and BS_ALLOCATED = BS_ALLOCATED) or (CAN_Engine.TX_DatagramRejected.State and BS_ALLOCATED = BS_ALLOCATED) end; procedure CANStorage_NMRAnetBufferStore(CANBuffer: PCANBuffer); begin Inc(CAN_Engine.TX_NMRAnetBufferLoaded); CAN_Engine.TX_NMRAnetBuffer[CAN_Engine.TX_NMRAnetBufferTail] := CANBuffer^; CAN_Engine.TX_NMRAnetBuffer[CAN_Engine.TX_NMRAnetBufferTail].State := CAN_Engine.TX_NMRAnetBuffer[CAN_Engine.TX_NMRAnetBufferTail].State or BS_ALLOCATED; Inc(CAN_Engine.TX_NMRAnetBufferTail); if CAN_Engine.TX_NMRAnetBufferTail >= MAX_NMRANET_BUFFER_SIZE then CAN_Engine.TX_NMRAnetBufferTail := 0; CAN_Engine.TX_NMRAnetBufferFull := CAN_Engine.TX_NMRAnetBufferTail = CAN_Engine.TX_NMRAnetBufferHead; // If we overlap after adding we are full end; procedure LockCANInterrupt; begin end; procedure UnLockCANInterrupt; begin end; procedure StartCANMessageEngine; begin // Net_Ethernet_28j60_startSendTCP(OlcbSocket); end; procedure StartCANHighPriorityMessageEngine; begin // Net_Ethernet_28j60_startSendTCP(OlcbSocket); end; var Receive_GridConnectBuffer: TGridConnectString; // Needs to persist between receptions Receive_GridConnectBufferIndex: Integer; RxCANBuffer: TCANBuffer; procedure Eth_UserProcess_Main; begin end; procedure Do_Storage_Send; var TxBuffer: PCANBuffer; GridConnectBuffer: array[MAX_GRID_CONNECT_LEN] of char; // ipDest: TIPAddress; // portDest: Word; // TCP_buf: array[1472] of byte; TCP_buf_start: ^byte; TCP_buf_size: word; OlcbPacketLength: Word; begin (* if SocketManager.Transmitter.Flags.SOCKET_TX_FLAG_LOADED = 0 then // Is the transmitter empty? begin TCP_buf_start := @SocketManager.Transmitter.DataBuffer; SocketManager.Transmitter.DataLength := 0; TxBuffer := CANStorage_NextHighPriorityToSend; // Pull the next item to send out of the list while (TxBuffer <> nil) do // Make sure the Buffer never gets bigger than the TCP packet can hold (1300). This should be easy since that is a LOT of CAN Storage buffers begin CAN_Engine.IsTransmitting := True; // Yes, placed something in the buffer CANBufferToGridConnect(TxBuffer, GridConnectBuffer); {$IFDEF LOG_UART1}UART_Write_Text('High Priority Tx: ' + GridConnectBuffer + LF);{$ENDIF} OlcbPacketLength := strlen(GridConnectBuffer); memcpy(TCP_buf_start, @GridConnectBuffer, OlcbPacketLength); TCP_buf_start := TCP_buf_start + OlcbPacketLength; TCP_buf_start^ := #10; Inc(TCP_buf_start); SocketManager.Transmitter.DataLength := SocketManager.Transmitter.DataLength + OlcbPacketLength + 1; TxBuffer^.State := BS_CLEAR; TxBuffer := CANStorage_NextHighPriorityToSend; end; TxBuffer := CANStorage_NextToSend; while (TxBuffer <> nil) do // Make sure the Buffer never gets bigger than the TCP packet can hold (1300). This should be easy since that is a LOT of CAN Storage buffers begin CAN_Engine.IsTransmitting := True; // Yes, placed something in the buffer CAN_Engine.TransmitImmediately := False; CANBufferToGridConnect(TxBuffer, GridConnectBuffer); {$IFDEF LOG_UART1}UART_Write_Text('Low Priority Tx: ' + GridConnectBuffer + LF);{$ENDIF} OlcbPacketLength := strlen(GridConnectBuffer); memcpy(TCP_buf_start, @GridConnectBuffer, OlcbPacketLength); TCP_buf_start := TCP_buf_start + OlcbPacketLength; TCP_buf_start^ := #10; Inc(TCP_buf_start); SocketManager.Transmitter.DataLength := SocketManager.Transmitter.DataLength + OlcbPacketLength + 1; TxBuffer^.State := BS_CLEAR; TxBuffer := CANStorage_NextToSend; end; // TCP_LoadBuffer(SocketManager.Transmitter.PhysicalLayerHeader, SocketManager.Transmitter.IPHeader, SocketManager.Transmitter.TCPHeader); end; *) (* TCP_buf_start := @TCP_buf[0]; TCP_buf_size := 0; // Send something if we can TxBuffer := CANStorage_NextHighPriorityToSend; // Pull the next item to send out of the list while (TxBuffer <> nil) do // Make sure the Buffer never gets bigger than the TCP packet can hold (1300). This should be easy since that is a LOT of CAN Storage buffers begin CAN_Engine.IsTransmitting := True; // Yes, placed something in the buffer CANBufferToGridConnect(TxBuffer, GridConnectBuffer); {$IFDEF LOG_UART1}UART_Write_Text('High Priority Tx: ' + GridConnectBuffer + LF);{$ENDIF} OlcbPacketLength := strlen(GridConnectBuffer); memCpy(TCP_buf_start, @GridConnectBuffer, OlcbPacketLength); TCP_buf_start := TCP_buf_start + OlcbPacketLength; TCP_buf_start^ := #10; Inc(TCP_buf_start); TCP_buf_size := TCP_buf_size + OlcbPacketLength + 1; TxBuffer^.State := BS_CLEAR; TxBuffer := CANStorage_NextHighPriorityToSend; end; TxBuffer := CANStorage_NextToSend; while (TxBuffer <> nil) do // Make sure the Buffer never gets bigger than the TCP packet can hold (1300). This should be easy since that is a LOT of CAN Storage buffers begin CAN_Engine.IsTransmitting := True; // Yes, placed something in the buffer CAN_Engine.TransmitImmediately := False; CANBufferToGridConnect(TxBuffer, GridConnectBuffer); {$IFDEF LOG_UART1}UART_Write_Text('Low Priority Tx: ' + GridConnectBuffer + LF);{$ENDIF} OlcbPacketLength := strlen(GridConnectBuffer); memCpy(TCP_buf_start, @GridConnectBuffer, OlcbPacketLength); TCP_buf_start := TCP_buf_start + OlcbPacketLength; TCP_buf_start^ := #10; Inc(TCP_buf_start); TCP_buf_size := TCP_buf_size + OlcbPacketLength + 1; TxBuffer^.State := BS_CLEAR; TxBuffer := CANStorage_NextToSend; end; if TCP_buf_size > 0 then begin Str2IP(OLCB_SERVER_IP, ipDest); portDest := OLCB_SERVER_PORT; // if Open_TCP_Connection(ipDest, portDest, 12022) = RESULT_OPEN_TCP_CONNECTION_OK then begin TCP_UserSend(ipDest, portDest, TCP_buf_size, TCP_buf); // Close_TCP_Connection(ipDest, portDest); end; end; *) end; procedure Eth_UserTCP(var dest_ip_addr_T : TIPAddress; var source_port_T, dest_port_T, PacketLen : word; iSocket : Integer); var TCP_Receive_Char: char; Socket: ^TSocket; begin // Socket will be released after this call CAN_Engine.IsTransmitting := False; // Maybe, maybe not we will see... Socket := @SocketManager.Socket[iSocket]; if Socket^.StateFlags.SOCKET_FLAG_CONNECTED = 1 then begin {$IFDEF LOG_UART} UART1_Write_Text('TCP Rx User Entered'+LF); WordToStr(Socket^.dest_port_S, s1); UART1_Write_Text('Dest Port: ' + s1 +LF); WordToStr(Socket^.Stop_addr - Socket^.Start_addr, s1); UART1_Write_Text('Data Size: ' + s1 +LF); WordToStr(PacketLen, s1); UART1_Write_Text('Packet Len Size: ' + s1 +LF); {$ENDIF} // Read something if we can if (Socket^.DestPort = 12021) and (PacketLen) then begin Receive_GridConnectBufferIndex := 0; while (Socket^.Stop_addr - Socket^.Start_addr > 0) do begin TCP_Receive_Char := Socket^.TCP_buf[Socket^.Start_addr]; // Get the next byte from the stack case TCP_Receive_State of TCP_STATE_SYNC_START : // Find a starting ':' begin if TCP_Receive_Char = ':' then begin Receive_GridConnectBufferIndex := 0; Receive_GridConnectBuffer[Receive_GridConnectBufferIndex] := ':'; Inc(Receive_GridConnectBufferIndex); TCP_Receive_State := TCP_STATE_SYNC_FIND_X end end; TCP_STATE_SYNC_FIND_X : begin if TCP_Receive_Char <> ':' then // Handle double ":"'s by doing nothing if the next byte is a ":", just wait for the next byte to see if it is a "X" begin if (TCP_Receive_Char = 'X') or (TCP_Receive_Char = 'x') then begin Receive_GridConnectBuffer[Receive_GridConnectBufferIndex] := 'X'; Inc(Receive_GridConnectBufferIndex); TCP_Receive_State := TCP_STATE_SYNC_FIND_HEADER end else TCP_Receive_State := TCP_STATE_SYNC_START // Error, start over end end; TCP_STATE_SYNC_FIND_HEADER : begin if IsValidHexChar(TCP_Receive_Char) then begin Receive_GridConnectBuffer[Receive_GridConnectBufferIndex] := TCP_Receive_Char; if Receive_GridConnectBufferIndex = 9 then TCP_Receive_State := TCP_STATE_SYNC_FIND_N; Inc(Receive_GridConnectBufferIndex); end else TCP_Receive_State := TCP_STATE_SYNC_START // Error start over end; TCP_STATE_SYNC_FIND_N : begin if (TCP_Receive_Char >= 'N') or (TCP_Receive_Char <= 'n') then begin Receive_GridConnectBuffer[Receive_GridConnectBufferIndex] := 'N'; Inc(Receive_GridConnectBufferIndex); TCP_Receive_State := TCP_STATE_SYNC_FIND_DATA; end else TCP_Receive_State := TCP_STATE_SYNC_START // Error start over end; TCP_STATE_SYNC_FIND_DATA : begin if TCP_Receive_Char = ';'then begin if (Receive_GridConnectBufferIndex + 1) mod 2 = 0 then // 0 index, add 1 for the actual character count begin Receive_GridConnectBuffer[Receive_GridConnectBufferIndex] := ';'; Receive_GridConnectBuffer[Receive_GridConnectBufferIndex + 1] := #0; GridConnectToCANBuffer(Receive_GridConnectBuffer, @RxCANBuffer); if RxCANBuffer.ID and MTI_OLCB_MSG = MTI_OLCB_MSG then begin {$IFDEF LOG_GRIDCONNECT_UART} UART_Write_Text('Olcb Rx: ' + Receive_GridConnectBuffer + LF); {$ENDIF} ReceivedOnFilter1(@RxCANBuffer) // SUCCESS SEND IT end else begin {$IFDEF LOG_GRIDCONNECT_UART} UART_Write_Text('CAN Rx: ' + Receive_GridConnectBuffer + LF); {$ENDIF} ReceivedOnFilter0(@RxCANBuffer); end; end; TCP_Receive_State := TCP_STATE_SYNC_START // Done end else begin if IsValidHexChar(TCP_Receive_Char) then begin Receive_GridConnectBuffer[Receive_GridConnectBufferIndex] := TCP_Receive_Char; Inc(Receive_GridConnectBufferIndex); end else TCP_Receive_State := TCP_STATE_SYNC_START; // Error start over end end else TCP_Receive_State := TCP_STATE_SYNC_START; // Invalidate State Index end; Inc(Socket^.Start_addr); // Next char end; end; end else begin {$IFDEF LOG_UART} UART1_Write_Text('TCP Rx User Entered, Socket not connected'+LF); {$ENDIF} end; end; function Eth_UserUDP(var dest_ip_addr_U : TIPAddress; var dest_port_U, source_port_U, len_U : word) : word; begin UART1_Write_Text('UDP'+LF); result := 0; end; procedure DisableInt; begin T3IE_bit := 0; end; procedure EnableInt; begin T3IE_bit := 1; end; end.