Quicklinks
Vienna Sanitation is a company that rents restrooms in public areas. To use one of their restrooms a 50 cent coin needs to be inserted in a counter. This will open the door to the restroom.
Every restroom has its own ID and a counter which counts how many coins have been inserted into the restroom. Every day the counters value is logged into a database and the money is stored somewhere safe. By comparing today's value with yesterday's value the finance department is able to calculate the yield.
This case study describes how Vienna Sanitation implemented an SMS-based solution to register the collected money on a central location, automatically, using SMS.
Currently the employees need to read out the counter status for all of the restrooms and send out a form to the finance department every day. This form contains the name of the employee, the restroom id and the ammount of money that was deposited. It usually takes at least two days for this form to arrive at the finance department. After which the employees at the finance department fill in the form details into a shared Excel sheet.
Vienna Sanitation wanted to improve the speed as well as the quality of the process by automating as much of it as was possible. To do this the restroom information should be sent to the finance department using SMS. This way the finance department does not need to wait two days to process the yield of the day. Sending the toilet information using SMS even allows a computer to process the submitted data automatically.
Goals of the new system:
The Vienna Sanitation cleaning employees report the counter value to Vienna Sanitation by sending a SMS message to a Vienna Sanitation server. This server processes the messages and stores the submitted counter values into the database. The message sender is checked first before the message itself is being processed. If the message is received from a known phone number, it will be processed. The employee should be able to report several results in only one SMS message. The submitted values will be entered into a Access database.
The solution is implemented using the ActiveXperts SMS Messaging Server There's a script to process incoming messages, a simple Access database and a properly configured GSM-modem with a SIM card.
The project database contains three tables:
A counter value should be sent in a fixed format. The first characters should indicate the toilet ID followed by a space and the counter value. For example counter value 365 for toilet number 5 should be entered this way: "5 365". The cleaning-woman is able to send several counter values by separating them with a dot. For example counter value 365 for station 5 and counter value 654 for station 6 should be entered this way: "5 356. 6 654".
The system handles SMS messages as follows:
| SMS syntax | Sample | Explanation |
|---|---|---|
| <toilet-number><turnover> | 5 356 | Turn-over of 356 for toilet 5 |
| <toilet-number 1><turnover 1>. <toilet-number 2><turnover 2>. <toilet-number n><turnover n> |
5 356.6 611.8 55.9.303 | Turn-over of 356 for toilet 5; Turn-over of 611 for toilet 6, etc. |
| <any other message> | Please help | Unrecognized command. A Help message is replied |
A Trigger is called when a new messages arrives in the system. Vienna Sanitation handles only one type of SMS messages: incoming turn-over registrations. Therefore, only one trigger is required:
| Enabled | Description | Condition | Script |
|---|---|---|---|
| YES | Process incoming commands | ANY MESSAGE | \Projects\SupportIT\Triggers\SupportIT.vbs |
Option Explicit
CONST STR_DEBUGFILE = "Sys\Tmp\Vienna Sanitation.txt"
CONST STR_DATABASEFILE = "Projects\Vienna Sanitation\Database\Vienna Sanitation.mdb"
Const SEPERATOR = "."
' Declaration of global objects
im g_objMessageDB, g_objDebugger, g_objConstants
' Creation of global objects
Set g_objConstants = CreateObject( "Axsms-messaging-server.Constants" )
Set g_objMessageDB = CreateObject( "Axsms-messaging-server.MessageDB" )
Set g_objDebugger = CreateObject( "ActiveXperts.VbDebugger" )
' Set Debug file - for troubleshooting purposes
g_objDebugger.DebugFile = STR_DEBUGFILE
g_objDebugger.Enabled = True
' // ========================================================================
' // Function: ProcessMessage
' // ------------------------------------------------------------------------
' // ProcessMessage trigger function to process incoming messages
' // ========================================================================
Function ProcessMessage( numMessageID )
Dim objMessageIn, objMessageOut
Dim numEmployeeID, numStationID, numTellerStand
Dim strEmployeeName, strErrorMessage
Dim bResult
Dim strMessageOutBody
Dim arrMessage, i
Dim strInvoerMethode
g_objDebugger.WriteLine ">> ProcessMessage"
' Open the Message Database
g_objMessageDB.Open
If( g_objMessageDB.LastError <> 0 ) Then
g_objDebugger.WriteLine "<< ProcessMessage, unable to open database"
Exit Function
End If
' Retrieve the message that has just been received. If it fails then exit script
Set objMessageIn = g_objMessageDB.FindFirstMessage ( "ID = " & numMessageID )
If g_objMessageDB.LastError <> 0 Then
g_objMessageDB.Close
g_objDebugger.WriteLine "<< ProcessMessage, FindFirstMessage failed: [" & _
g_objMessageDB.LastError & "]"
Exit Function
End If
' Change Status to from Pending to Success.
objMessageIn.Status = g_objConstants.MESSAGESTATUS_SUCCESS
g_objMessageDB.Save objMessageIn
g_objDebugger.WriteLine "Incoming message saved, result: [" & g_objMessageDB.LastError & "]"
' Retrieve Member's name
bResult = CheckEmployee( objMessageIn.From, numEmployeeID, strEmployeeName, strErrorMessage )
' Convert the message to an array
arrMessage = MessageToArray( objMessageIn.Body )
' Check whether the user input is valid or not
If( objMessageIn.Body <> "" ) Then
If( IsValidMessage( arrMessage ) ) Then
' Loop throught the results and process them
For i = 0 To UBound( arrMessage )
If( bResult ) Then
bResult = InsertCollected( numEmployeeID, arrMessage(i,0), arrMessage(i,1), _
strErrorMessage )
Else
Exit For
End If
Next
' Reply to the customer
If( bResult = False ) Then
strMessageOutBody = strErrorMessage
Else
strMessageOutBody = "Dear " & strEmployeeName & ", we received your counter info " & _
PrintResults( arrMessage ) & " successfully."
End If
Else
strMessageOutBody = "Invalid message. Provide toilet-id, followed by a blank, " & _
"followed by the counter info"
End If
End If
If( strMessageOutBody <> "" ) Then
ReplyMessage objMessageIn.From, strMessageOutBody
End If
' Close the Message Database
g_objMessageDB.Close
g_objDebugger.WriteLine "<< ProcessMessage"
End Function
' // ========================================================================
Function ReplyMessage( strRecipient, strBody )
Dim objMessageOut
g_objDebugger.WriteLine ">> ReplyMessage"
' Create the reply message
Set objMessageOut = g_objMessageDB.Create
If( g_objMessageDB.LastError = 0 ) Then
objMessageOut.Direction = g_objConstants.MESSAGEDIRECTION_OUT
objMessageOut.Type = g_objConstants.MESSAGETYPE_SMS
objMessageOut.Status = g_objConstants.MESSAGESTATUS_PENDING
objMessageOut.To = strRecipient
objMessageOut.ChannelID = 0
objMessageOut.Body = strBody
g_objMessageDB.Save objMessageOut
End If
g_objDebugger.WriteLine "<< ReplyMessage"
End Function
' // ========================================================================
' // CheckEmployee
' // ------------------------------------------------------------------------
' // Check if a mobile number is in the Members database to confirm that the
' // person is a member
' // ========================================================================
Function CheckEmployee( strInputNumber, ByRef numEmployeeID, ByRef strEmployeeName, _
ByRef strErrorMessage )
Dim objConn, RS, strQuery
g_objDebugger.WriteLine( ">> CheckEmployee" )
CheckEmployee = False
numEmployeeID = 0
strEmployeeName = ""
Set objConn = GetDatabase()
Set RS = objConn.Execute( "SELECT * FROM tblEmployees WHERE Cell='" & strInputNumber & "'" )
If RS.EOF Then
CheckEmployee = False
numEmployeeID = 0
strEmployeeName = ""
strErrorMessage = "Error: Unknown user!"
Else
CheckEmployee = True
numEmployeeID = RS( "ID" )
strEmployeeName = RS( "Name" )
strErrorMessage = ""
End If
objConn.Close
Set objConn = Nothing
g_objDebugger.WriteLine( "<< CheckEmployee" )
End Function
' // ========================================================================
' // Query InsertOpgave
' // ------------------------------------------------------------------------
' // Lookup the current price of the selected stock ( use ticker symbol )
' // ========================================================================
Function InsertCollected( numEmployeeID, numStationID, numCounter, ByRef strErrorMessage )
g_objDebugger.WriteLine( ">> InsertCollected" )
Dim objConn, RS
Dim strQuery
InsertCollected = False
strErrorMessage = ""
Set objConn = GetDatabase()
strQuery = "INSERT INTO tblCounter( EmployeeID, StationID, CoinsCounter ) VALUES( " & _
numEmployeeID & ", " & numStationID & ", " & numCounter & ")"
g_objDebugger.WriteLine( ">> Inserting into database: " & strQuery )
objConn.Execute ( strQuery )
' Close database
objConn.Close
Set objConn = Nothing
InsertCollected = True
g_objDebugger.WriteLine( "<< InsertCollected" )
End Function
' // ========================================================================
' // MessageToArray
' // ------------------------------------------------------------------------
' // Change the current message in a 2 dimentional array
' // ========================================================================
Function MessageToArray( strMessageBody )
' Declare array to store results in
Dim arrResult()
Dim arrValues, arrMessage, i
' In case 2 or more counter results are submitted a 2 dimentional
' array needs to be reserved
' Split the messages into seperate results
arrMessage = Split( strMessageBody, SEPERATOR )
' Count how many results are returned and redeclare an array
ReDim arrResult( UBound(arrMessage), 1 )
' Read the message and store it into an array
For i = 0 To UBound( arrMessage )
If( InStr( arrMessage( i ), " " ) ) Then
arrValues = Split( Trim(arrMessage( i )), " " )
On Error Resume Next
arrResult(i,0) = arrValues(0)
arrResult(i,1) = arrValues(1)
On Error Goto 0
End If
Next
' Return this array
MessageToArray = arrResult
End Function
' // ========================================================================
' // PrintResultaten
' // ------------------------------------------------------------------------
' // Show the results
' // ========================================================================
Function PrintResults( arrResult )
Dim strResults, i
For i = 0 To UBound( arrResult )
strResults = strResults & arrResult(i,1) & " for station " & arrResult(i,0)
If( i <= ( UBound( arrResult ) -1 ) ) Then
strResults = strResults & " and "
End If
Next
PrintResults = strResults
End Function
' // ========================================================================
' // IsValidMessage
' // ------------------------------------------------------------------------
' // Check whether the user input is correct or not
' // ========================================================================
Function IsValidMessage( arrResultaten )
Dim bResult, objConn, RS, i
bResult = True
' Op the database
Set objConn = GetDatabase()
For i = 0 To UBound( arrResultaten )
' Check wheter the station exists or not
If( Not IsNumeric(arrResultaten(i,0) ) ) Then
bResult = False
Exit For
End If
Set RS = objConn.Execute( "SELECT Count(*) FROM tblStations WHERE ID=" & arrResultaten(i,0) )
If( RS(0) < 1 ) Then
bResult = False
Exit For
End If
' Check whether the counter value is correctly
If( Not IsNumeric( arrResultaten(i,1) ) ) Then
bResult = False
Exit For
End If
If( Trim(arrResultaten(i,1)) = "" ) Then
bResult = False
Exit For
End If
If( arrResultaten(i,1) < 0 ) Then
bResult = False
Exit For
End If
Next
' Database closen
objConn.Close
Set objConn = Nothing
' Return the results
IsValidMessage = bResult
End Function
' // ========================================================================
' // GetDatabase
' // ------------------------------------------------------------------------
' // Try to connect to the project database
' // ========================================================================
Function GetDatabase()
g_objDebugger.WriteLine( ">> GetDatabase" )
'Declare variables
Dim numErrNo
Dim objDB
'Set the FileSystemObject and the databaseobject
Set objDB = CreateObject("ADODB.Connection")
'Try to open the database
On Error Resume Next
objDB.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & STR_DATABASEFILE & ";"
numErrNo = Err.Number
On Error Goto 0
'If unable to succesfully do so, quit the script!
If( numErrNo <> 0 ) Then
g_objDebugger.WriteLine( ">> GetDatabase: Cannot find database.." )
WScript.Quit
'Otherwise, return the databaseobject
Else
Set GetDatabase = objDB
End If
g_objDebugger.WriteLine( "<< GetDatabase" )
End Function