CLASS TSocket
CLASSDATA lInitialised INIT .F.
DATA sIPAddress
DATA iPortNumber
DATA pSocket
DATA lConnected
DATA cData
METHOD New() CONSTRUCTOR
METHOD SetIP( sIPAddress )
METHOD SetPort( iPortNumber )
METHOD Connect()
METHOD Send( cMessage )
METHOD Receive()
METHOD Close()
METHOD SendReceive( cMessage )
METHOD CleanUp()
ENDCLASS
METHOD New() CLASS TSocket
::sIPAddress := "127.0.0.1"
::iPortNumber := 1800
IF !::lInitialised
INetInit()
::lInitialised := .T.
ENDIF
RETURN self
METHOD SetIP( sIPAddress ) CLASS TSocket
::sIPAddress := sIPAddress
RETURN nil
METHOD SetPort( iPortNumber ) CLASS TSocket
::iPortNumber := iPortNumber
RETURN nil
METHOD Connect() CLASS TSocket
lOK := .T.
TRY
::pSocket := INetConnect( ::sIPAddress, 1800 )
CATCH
MsgInfo( "Unable to connect to data server" )
lOK := .F.
END
IF INetErrorCode( ::pSocket ) <> 0
? "Socket error:", INetErrorDesc( ::pSocket )
INetCleanUp()
lOK := .F.
// QUIT
ENDIF
IF lOK
::lConnected := .T.
ELSE
::lConnected := .F.
ENDIF
RETURN lOK
METHOD Send( cMessage ) CLASS TSocket
INetSend( ::pSocket, cMessage )
RETURN nil
METHOD Receive() CLASS TSocket
LOCAL cBuffer
LOCAL nBytes
::cData := ""
nBytes := 1
DO WHILE nBytes > 0
cBuffer := Space( 1024 )
nBytes := INetRecv( ::pSocket, @cBuffer )
::cData += Left( cBuffer, nBytes )
ENDDO
RETURN nil
METHOD Close() CLASS TSocket
INetClose( ::pSocket )
RETURN nil
METHOD SendReceive( cMessage ) CLASS TSocket
::Send( cMessage )
::Receive()
::Close()
RETURN ::cData
METHOD CleanUp() CLASS TSocket
RETURN InetCleanUp()
oSocket := TSocket():New()
oSocket:SetIP( sIPAddress )
oSocket:SetPort( 1800 )
#define query_PT_LIST_BY_NAME 1
#define query_PT_BY_KEY 2
#define query_PT_WRITE 3
etc.....
oSocket:Connect()
aREQUEST[1] := query_PT_LIST_BY_NAME
aREQUEST[2] := Array( 1 )
aREQUEST[2][1] := ::sSearch
oSocket:Send( HB_Serialize( aREQUEST) )
oSocket:Receive()
oSocket:Close()
aTranslated := HB_Deserialize( oSocket:cData )
aSTATUS := aTranslated[1]
aOUTPUT := aTranslated[2]
iList := LEN( aLIST[2] )
::aKey := aLIST[2]
::aFName := aLIST[3]
::aGName := aLIST[4]
::aDOB := aLIST[5]
::aGender := aLIST[6]
LOCAL aREQUEST
oSocket:Connect()
aREQUEST := ARRAY( 2 )
aREQUEST[1] := query_PT_BY_KEY
aREQUEST[2] := Array( 1 )
aREQUEST[2][1] := ::sKey
oSocket:Send( HB_Serialize( aREQUEST) )
oSocket:Receive()
oSocket:Close()
aTranslated := HB_Deserialize( oSocket:cData )
aSTATUS := aTranslated[1]
aOUTPUT := aTranslated[2]
__ObjSetValueList( self, aOUTPUT )
::dDOB := SToD( ::sDOB )
{ { <variable-name-1>, <variable-1-value> }, { <variable-name-2>, <variable-2-value> }, ............. { <variable-name-n>, <variable-n-value> } }
xProgrammer wrote:I guess I have sort of wandered off the topic a bit! I put the topic here because I thought some others might be interested in the approach I am taking. But I'm quite happy to push on regardless. In fact to date the journey has been rather exciting. Does that make me strange? Probably. But I can live with that.
CLASS TData
DATA iWorkArea
DATA aPROPERTIES
DATA iProperties
DATA aOUTPUT
DATA aLIST
DATA lFound
DATA iRecord
DATA sName
METHOD New() CONSTRUCTOR
ENDCLASS
METHOD New() CLASS TData
RETURN self
CLASS TListData FROM TData
METHOD New() CONSTRUCTOR
METHOD SetUp()
METHOD ClearData()
METHOD ReadRecord()
METHOD IndexRead()
ENDCLASS
METHOD New( iWorkArea, aProperties, sName ) CLASS TListData
::iWorkArea := iWorkArea
::aPROPERTIES := aProperties
IF PCount() > 2
::sName := sName
ELSE
::sName := ""
ENDIF
::SetUp()
RETURN self
METHOD SetUp() CLASS TListData
LOCAL ii
LOCAL aTHIS
SELECT ( ::iWorkArea )
::iProperties := LEN( ::aPROPERTIES )
IF lVerbose
?
? "Setting Up List Data ", ::sName, " property count ", LTRIM( STR( ::iProperties ) )
ENDIF
::aLIST := Array( ::iProperties + 1 )
::aLIST[1] := ARRAY( ::iProperties )
FOR ii = 1 TO ::iProperties
::aLIST[1][ii] := ::aPROPERTIES[ii][2]
::aPROPERTIES[ii][3] := FieldPos( ::aPROPERTIES[ii][1] )
IF lVerbose
? "Field ", ::aPROPERTIES[ii][1], " found at ", ::aPROPERTIES[ii][3], " returned as ", ::aPROPERTIES[ii][2]
ENDIF
::aLIST[ii + 1] := ARRAY( 0 )
NEXT
RETURN nil
METHOD IndexRead( iOrder, sSearch, bCompare ) CLASS TListData
LOCAL vTest
SELECT ( ::iWorkArea )
::ClearData()
SET ORDER TO ( iOrder )
SEEK sSearch
::lFound := FOUND()
IF ::lFound
lLoop := .T.
DO WHILE lLoop
IF lVerbose
? "Found ", sSearch, " at record ", RecNo(), " in ", ::sName
ENDIF
::ReadRecord()
SKIP
IF EOF()
lLoop := .F.
IF lVerbose
? "End of file reached in ", ::sName
ENDIF
ELSE
vTest := Eval( bCompare )
IF vTest <> sSearch
lLoop := .F.
IF lVerbose
? "No match at record ", RecNo(), " in ", ::sName
ENDIF
ENDIF
ENDIF
ENDDO
ELSE
IF lVerbose
? "Failed to find ", sSearch, " in ", ::sName
ENDIF
ENDIF
RETURN nil
METHOD ClearData() CLASS TListData
LOCAL ii
FOR ii = 2 TO ::iProperties + 1
ASize( ::aLIST[ii], 0 )
NEXT
RETURN nil
METHOD ReadRecord() CLASS TListData
SELECT ( ::iWorkArea )
FOR ii = 1 TO ::iProperties
cDataType := FieldType( ::aProperties[ii][3] )
DO CASE
CASE cDataType = "D"
AAdd( ::aLIST[ii + 1], STOD( FieldGet( ::aPROPERTIES[ii][3] ) ) )
OTHERWISE
AAdd( ::aLIST[ii + 1], FieldGet( ::aPROPERTIES[ii][3] ) )
ENDCASE
IF lVerbose
? ::aLIST[1][ii], " = ", ::aLIST[ii + 1][Len( ::aLIST[ii + 1] )]
ENDIF
NEXT
RETURN nil
CLASS TSingleData FROM TData
METHOD New() CONSTRUCTOR
METHOD SetUp()
METHOD ReadRecord()
METHOD KeyRead()
METHOD Write()
METHOD WriteRecord()
ENDCLASS
METHOD New( iWorkArea, aProperties, iKeyAllocator, sName ) CLASS TSingleData
::iWorkArea := iWorkArea
::aPROPERTIES := aProperties
IF PCount() > 3
::sName := sName
ELSE
::sName := ""
ENDIF
::SetUp()
RETURN self
METHOD Setup() CLASS TSingleData
LOCAL ii
LOCAL aTHIS
SELECT ( ::iWorkArea )
::iProperties := LEN( ::aPROPERTIES )
IF lVerbose
? "Setting Up Single Data ", ::sName, " property count ", LTRIM( STR( ::iProperties ) )
ENDIF
::aOUTPUT := ARRAY( ::iProperties )
FOR ii = 1 TO ::iProperties
aTHIS := Array( 2 )
aTHIS[1] := ::aPROPERTIES[ii][2]
::aOUTPUT[ii] := aTHIS
::aPROPERTIES[ii][3] := FieldPos( ::aPROPERTIES[ii][1] )
IF lVerbose
? "Field ", ::aPROPERTIES[ii][1], " found at position ", ::aPROPERTIES[ii][3], " returned as ", ::aPROPERTIES[ii][2]
ENDIF
NEXT
RETURN nil
METHOD ReadRecord() CLASS TSingleData
SELECT ( ::iWorkArea )
FOR ii = 1 TO ::iProperties
::aOUTPUT[ii][2] := FieldGet( ::aPROPERTIES[ii][3] )
? ::aOUTPUT[ii][1], " = ", ::aOUTPUT[ii][2]
NEXT
RETURN nil
METHOD KeyRead( iOrder, sKey ) CLASS TSingleData
SELECT ( ::iWorkArea )
SET ORDER TO ( iOrder )
SEEK sKey
::lFound := FOUND()
IF ::lFound
IF lVerbose
? "Found ", sKey, " at ", RecNo(), " in ", ::sName
ENDIF
::iRecord := RecNo()
::ReadRecord()
aRESPONSE[2] := ::aOUTPUT
ELSE
IF lVerbose
? "ERROR: Failed to find ", sKey, " in ", ::sName
ENDIF
ENDIF
RETURN nil
METHOD Write( iOrder, aRESPONSE, sKey ) CLASS TSingleData
SELECT ( ::iWorkArea )
SET ORDER TO ( iOrder )
IF aRESPONSE[1][2] = "["
? "Insert Required"
APPEND BLANK
aRESPONSE[1][2] := "999999999999001"
ELSE
? "Update Required"
SEEK sKey
::lFound := FOUND()
IF ::lFound
? "Record Found"
::WriteRecord( aRESPONSE )
ELSE
? "ERROR: Unable to locate record for update"
ENDIF
ENDIF
RETURN nil
METHOD WriteRecord( aOUTPUT ) CLASS TSingleData
LOCAL ii
LOCAL iPosition
FOR ii = 1 TO LEN( aOUTPUT )
iPosition := FieldPos( aOUTPUT[ii][1] )
IF iPosition > 0
FieldPut( iPosition, aOUTPUT[ii][2] )
ENDIF
NEXT
RETURN nil
oPT_BY_KEY := TSingleData():New( 1, { { "PT_KEY", "sKey", 0 }, { "PT_NMFAMLY", "sNmFamly", 0 }, { "PT_NMGIVEN", "sNmGiven", 0 }, ;
{ "PT_NMOTHER", "sNmOther", 0 }, { "PT_NMPREV", "sNmPrev", 0 }, { "PT_NMPREF", "sNmPref", 0 }, { "PT_NMTITLE", "sNmTitle", 0 }, ;
{ "PT_DOB", "sDOB", 0 }, { "PT_GENDER", "cGender", 0 }, { "PT_ADLINE1", "sAdLine1", 0 }, { "PT_ADLINE2", "sAdLine2", 0 }, ;
{ "PT_ADSUBRB", "sAdSubrb", 0 }, { "PT_ADSTATE", "sAdState", 0 }, { "PT_ADPCODE", "sAdPCode", 0 }, { "PT_ADCNTRY", "sAdCntry", 0 }, ;
{ "PT_PHHOME", "sPhHome", 0 }, { "PT_PHWORK", "sPhWork", 0 }, { "PT_PHMOB", "sPhMob", 0 }, { "PT_PHFAX", "sPhFax", 0 }, ;
{ "PT_EMAIL", "sEmail", 0 }, { "PT_MEDIC", "sMedic", 0 }, { "PT_MEDPOS", "sMedPos", 0 }, { "PT_VETAFF", "sVetAff", 0 }, ;
{ "PT_ACTIVE", "cActive", 0 }, { "PT_LUBY", "sLUBy", 0 }, { "PT_LUWHEN", "sLUWhen", 0 }, { "PT_LUACTN", "cLUActn", 0 } }, ;
1, "Patient by Key" )
oPT_LIST_BY_NAME := TListData():New( 1, { { "PT_KEY", "sKey", 0 }, { "PT_NMFAMLY", "sNmFamly", 0 }, { "PT_NMGIVEN", "sNmGiven", 0 }, ;
{ "PT_DOB", "dDOB", 0 }, { "PT_GENDER", "cGender", 0 } }, "Patient List by Name" )
oPF_LIST_BY_PT_KEY := TListData():New( 3, { { "PF_KEY", "aKey", 0 }, { "PF_FLKEY", "aFLKey", 0 }, { "PF_FLKEY", "aFLName", -1, 2, 1 }, ;
{ "PF_DTFIRST", "aDtFirst", 0 }, { "PF_DTLAST", "aDtLast", 0 }, { "PF_CLOSED", "aClosed", 0 } }, "Patient File List by Patient Key")
aREQUEST := HB_Deserialize( cData )
IF lVerbose
? "Received request number: ", aREQUEST[1]
ENDIF
iSelection := aREQUEST[1]
DO CASE
CASE iSelection = query_PT_LIST_BY_NAME
oPT_LIST_BY_NAME:IndexRead( 2, aREQUEST[2][1], { | | UPPER( PT_NMFAMLY + PT_NMGIVEN ) } )
CASE iSelection = query_PT_BY_KEY
oPT_BY_KEY:KeyRead( 1, aREQUEST[2][1] )
aRESPONSE[2] := oPT_BY_KEY:aOUTPUT
CASE iSelection = query_PT_WRITE
oPT_BY_KEY:Write( 1, aREQUEST[2], aREQUEST[2][1][2] )
CASE iSelection := query_PF_LIST_BY_PT_KEY
oPF_BY_PT_KEY:Read( 2, aREQUEST[2][1] )
OTHERWISE
// error handling required here
ENDCASE
IF lVerbose
? "Press [Ctl-C] to quit"
ENDIF
INetInit()
// listen on port 1800
pServer := INetServer( 1800 )
INetSetTimeout( pServer, 500 )
? "Server up and ready for requests", pServer
? "Press [Ctl-C] to quit"
lContinue := .T.
DO WHILE lContinue
// wait for incoming connection requests
pClient := INetAccept( pServer )
IF INetErrorCode( pServer ) == 0
// process client request
// possibly in a future version in a separate thread
ServeClient( pClient )
ENDIF
ENDDO
// WaitForThreads() would go here in a threded version
// close socket and cleanup memory
INetClose( pServer )
INetCleanup()
CLASS TQuery
DATA lSuccess
DATA aREQUEST
DATA aTranslated
METHOD New() CONSTRUCTOR
METHOD Execute()
ENDCLASS
METHOD New() CLASS TQuery
::aREQUEST := Array( 2 )
RETURN self
METHOD Execute( iQuery, aParameters) CLASS TQuery
::aREQUEST[1] := iQuery
::aREQUEST[2] := aParameters
oSocket:Connect()
oSocket:Send( HB_Serialize( ::aREQUEST) )
oSocket:Receive()
oSocket:Close()
::aTranslated := HB_Deserialize( oSocket:cData )
// For now
::lSuccess := .T.
RETURN ::lSuccess
oQuery:Execute( query_PT_LIST_BY_NAME, { ::sSearch } )
aSTATUS := oQuery:aTranslated[1]
aLIST := oQuery:aTranslated[2]
CLASS TServerQuery
DATA iErrorLevel
DATA aErrors
DATA aResponse
DATA aRequest
METHOD New()
METHOD Request( pClient )
METHOD FlagError( iErrorLevel, iErrorNumber )
ENDCLASS
METHOD New()
::aErrors := Array( 0 )
RETURN self
METHOD Request( pClient ) CLASS TServerQuery
LOCAL cBuffer
LOCAL nBytes
LOCAL cData
LOCAL aOUTPUT := ARRAY( 0 )
LOCAL aSTATUS := ARRAY( 3 )
::iErrorLevel := 0
ASize( ::aErrors, 0 )
::aResponse := Array( 3 )
? "Serving:", INetAddress( pClient )
cData := ""
lReceiving := .T.
INetSetTimeout( pClient, 100 )
DO WHILE lReceiving
cBuffer := Space( 4096 )
nBytes := INetRecv( pClient, @cBuffer )
? "Bytes received:", nBytes
IF nBytes < 1
lReceiving := .F.
ELSE
cData += Left( cBuffer, nBytes )
ENDIF
ENDDO
::aREQUEST := HB_Deserialize( cData )
IF lVerbose
? "Received request number: ", ::aREQUEST[1]
ENDIF
aSTATUS[1] := 0
iSelection := ::aREQUEST[1]
HB_Exec( aCompiled[iSelection], nil )
IF lVerbose
? "Press [Ctl-C] to quit"
ENDIF
aSTATUS[1] := ::iErrorLevel
aSTATUS[2] := 1
aSTATUS[3] := ""
::aRESPONSE[1] := aSTATUS
cData := HB_Serialize( ::aRESPONSE )
INetSend( pClient, cData )
RETURN nil
METHOD FlagError( iErrorLevel, iErrorNumber ) CLASS TServerQuery
::iErrorLevel := MAX( ::iErrorLevel, iErrorLevel )
IF PCount() > 1
AAdd( ::aErrors, iErrorNumber )
ENDIF
RETURN ni
?
? "Setting up sockets"
INetInit()
// listen on port 1800
pServer := INetServer( 1800 )
INetSetTimeout( pServer, 500 )
? "Server up and ready for requests", pServer
? "Press [Ctl-C] to quit"
lContinue := .T.
DO WHILE lContinue
// wait for incoming connection requests
pClient := INetAccept( pServer )
IF INetErrorCode( pServer ) == 0
// process client request
// possibly in a future version in a separate thread
//ServeClient( pClient )
oQuery:Request( pClient )
ENDIF
ENDDO
// WaitForThreads() would go here in a threaded version
// close socket and cleanup memory
INetClose( pServer )
INetCleanup()
RETURN
Return to FiveWin for Harbour/xHarbour
Users browsing this forum: Google [Bot] and 54 guests