Index: /trunk/webapps/dynamicdata/unifiedlog.csv
===================================================================
--- /trunk/webapps/dynamicdata/unifiedlog.csv	(revision 440)
+++ /trunk/webapps/dynamicdata/unifiedlog.csv	(revision 440)
@@ -0,0 +1,6 @@
+0:03:00, CMS Activated.,S I-405 18.72 Edwards, 'AMAZINGLY FEW:DISCOS::::'
+0:03:00, CMS Activated.,N I-405 15.18 MAGNOLIA ST, 'SLOW FOR THE::CONE ZONE:::'
+0:03:00, CMS Activated.,S I-405 12.04 NEW HAMPSHIRE, 'SLOW TRAFFIC:AHEAD:USE CAUTION:::'
+0:03:00, CAD log, Incident #181, Henry: HELLO FROM EARTH
+0:03:50, CMS Deactivated., S I-405 18.72 Edwards
+0:04:30, CMS Activated.,S I-405 18.72 Edwards, 'EAT MORE CHIKIN:::::'
Index: /trunk/webapps/dynamicdata/cms_messages.json
===================================================================
--- /trunk/webapps/dynamicdata/cms_messages.json	(revision 431)
+++ /trunk/webapps/dynamicdata/cms_messages.json	(revision 440)
@@ -12,5 +12,5 @@
 {"cms":{"index":"20","message":{"displayTime":"","phase1":{"Line1":"","Line2":"","Line3":""},"phase2":{"Line1":"","Line2":"","Line3":""}}}},
 {"cms":{"index":"21","message":{"displayTime":"","phase1":{"Line1":"","Line2":"","Line3":""},"phase2":{"Line1":"","Line2":"","Line3":""}}}},
-{"cms":{"index":"23","message":{"displayTime":"","phase1":{"Line1":"AMAZINGLY FEW","Line2":"DISCOS","Line3":""},"phase2":{"Line1":"","Line2":"","Line3":""}}}},
+{"cms":{"index":"23","message":{"displayTime":"","phase1":{"Line1":"EAT MORE CHIKIN","Line2":"","Line3":""},"phase2":{"Line1":"","Line2":"","Line3":""}}}},
 {"cms":{"index":"24","message":{"displayTime":"","phase1":{"Line1":"","Line2":"","Line3":""},"phase2":{"Line1":"","Line2":"","Line3":""}}}},
 {"cms":{"index":"25","message":{"displayTime":"","phase1":{"Line1":"","Line2":"","Line3":""},"phase2":{"Line1":"","Line2":"","Line3":""}}}},
Index: /trunk/webapps/dynamicdata/highway_status.json
===================================================================
--- /trunk/webapps/dynamicdata/highway_status.json	(revision 434)
+++ /trunk/webapps/dynamicdata/highway_status.json	(revision 440)
@@ -4304,5 +4304,5 @@
        },
    "properties": 
-       {"street": "PAULARINO 2", "color": "lime", "perpx": "0.725068", "perpy": "-0.688678"}
+       {"street": "PAULARINO 2", "color": "yellow", "perpx": "0.725068", "perpy": "-0.688678"}
 },  
 {
@@ -4315,5 +4315,5 @@
        },
    "properties": 
-       {"street": "PAULARINO 2", "color": "lime", "perpx": "0.719864", "perpy": "-0.694115"}
+       {"street": "PAULARINO 2", "color": "yellow", "perpx": "0.719864", "perpy": "-0.694115"}
 },  
 {
@@ -4766,5 +4766,5 @@
        },
    "properties": 
-       {"street": "MACARTHU2", "color": "yellow", "perpx": "-0.710326", "perpy": "0.703873"}
+       {"street": "MACARTHU2", "color": "red", "perpx": "-0.710326", "perpy": "0.703873"}
 },  
 {
@@ -4777,5 +4777,5 @@
        },
    "properties": 
-       {"street": "DYER 1", "color": "yellow", "perpx": "0", "perpy": "0"}
+       {"street": "DYER 1", "color": "red", "perpx": "0", "perpy": "0"}
 },  
 {
@@ -4788,5 +4788,5 @@
        },
    "properties": 
-       {"street": "DYER 2", "color": "lime", "perpx": "-0.708914", "perpy": "0.705295"}
+       {"street": "DYER 2", "color": "red", "perpx": "-0.708914", "perpy": "0.705295"}
 },  
 {
@@ -4799,5 +4799,5 @@
        },
    "properties": 
-       {"street": "WARNER", "color": "lime", "perpx": "-0.711107", "perpy": "0.703084"}
+       {"street": "WARNER", "color": "red", "perpx": "-0.711107", "perpy": "0.703084"}
 },  
 {
@@ -4810,5 +4810,5 @@
        },
    "properties": 
-       {"street": "EDINGER 1", "color": "lime", "perpx": "0", "perpy": "0"}
+       {"street": "EDINGER 1", "color": "yellow", "perpx": "0", "perpy": "0"}
 },  
 {
@@ -12013,5 +12013,5 @@
        },
    "properties": 
-       {"street": "JAMBOREE1", "color": "lime", "perpx": "0.400805", "perpy": "0.916164"}
+       {"street": "JAMBOREE1", "color": "yellow", "perpx": "0.400805", "perpy": "0.916164"}
 },  
 {
@@ -12024,5 +12024,5 @@
        },
    "properties": 
-       {"street": "JAMBOREE2", "color": "lime", "perpx": "0.400805", "perpy": "0.916164"}
+       {"street": "JAMBOREE2", "color": "red", "perpx": "0.400805", "perpy": "0.916164"}
 },  
 {
@@ -12035,5 +12035,5 @@
        },
    "properties": 
-       {"street": "MACARTHUR 1", "color": "lime", "perpx": "0.408917", "perpy": "0.912572"}
+       {"street": "MACARTHUR 1", "color": "red", "perpx": "0.408917", "perpy": "0.912572"}
 },  
 {
@@ -12046,5 +12046,5 @@
        },
    "properties": 
-       {"street": "AIRPORT", "color": "lime", "perpx": "0.321527", "perpy": "0.9469"}
+       {"street": "AIRPORT", "color": "red", "perpx": "0.321527", "perpy": "0.9469"}
 },  
 {
@@ -12057,5 +12057,5 @@
        },
    "properties": 
-       {"street": "HOV AT 55N FLYOVER**", "color": "lime", "perpx": "0.271933", "perpy": "0.962316"}
+       {"street": "HOV AT 55N FLYOVER**", "color": "red", "perpx": "0.271933", "perpy": "0.962316"}
 },  
 {
@@ -12068,5 +12068,5 @@
        },
    "properties": 
-       {"street": "RED HILL", "color": "lime", "perpx": "0.126754", "perpy": "0.991934"}
+       {"street": "RED HILL", "color": "red", "perpx": "0.126754", "perpy": "0.991934"}
 },  
 {
Index: unk/webapps/dynamicdata/unifiedlog.html
===================================================================
--- /trunk/webapps/dynamicdata/unifiedlog.html	(revision 433)
+++ 	(revision )
@@ -1,9 +1,0 @@
-<HTML><HEAD><meta http-equiv="refresh" content="5" /></HEAD><BODY><PRE>0:07:26 CMS Activated. S I-405 18.72 Edwards 'AMAZINGLY FEW,DISCOS,,,,'
-0:07:26 CMS Activated. N I-405 15.18 MAGNOLIA ST 'SLOW FOR THE,,CONE ZONE,,,'
-0:07:26 CMS Activated. S I-405 12.04 NEW HAMPSHIRE 'SLOW TRAFFIC,AHEAD,USE CAUTION,,,'
-0:07:26 CAD log entry, Incident #181, AAA: HELLO WORLD
-0:07:26 CAD log entry, Incident #181, AAA: SEND PIZZA
-0:07:26 CAD log entry, Incident #181, AAA: ABLE BAKER
-0:07:26 CAD log entry, Incident #181, AAA: EMANCIPATE
-0:07:26 CAD log entry, Incident #181, AAA: HEY EY
-0:07:26 CAD log entry, Incident #181, AAA: SUPER DUPER
Index: /trunk/webapps/cptms/js/harLayer.js
===================================================================
--- /trunk/webapps/cptms/js/harLayer.js	(revision 426)
+++ /trunk/webapps/cptms/js/harLayer.js	(revision 440)
@@ -77,5 +77,5 @@
         var response1 = document.getElementById('har-msgcontent1').value.trim();
 
-        var newMsg = response1.replace(/;/gi, "");  // remove semicolons from input
+        var newMsg = response1.replace(/[;:",]/gi, "");  // remove semicolons from input
 
         if (newMsg.length == 0)
@@ -85,7 +85,7 @@
         else
         {
-            document.getElementById('har-msgdisplay1').value = response1;
+            document.getElementById('har-msgdisplay1').value = newMsg;
 
-            saveHARMessage(response1);
+            saveHARMessage(newMsg);
         }
     }
Index: /trunk/webapps/cptms/js/cmsLayer.js
===================================================================
--- /trunk/webapps/cptms/js/cmsLayer.js	(revision 426)
+++ /trunk/webapps/cptms/js/cmsLayer.js	(revision 440)
@@ -90,18 +90,21 @@
         // apply the padding
         var padding = kBlanks.substring(0, padLen);
-        // Fix defect #122.   Remove semicolons from input.
-        message = message.replace(/;/gi, ""); 
         return padding + message;
     }
-
+    // Remove harmful characters from the message
+    // Fix defect #122.   
+    function sanitize(text)
+    {
+        return text.replace(/[;:,"]/gi, ""); 
+    }
     function handleCMSsubmit()
     {
         // recover the user's response
-        var response1 = document.getElementById('msgcontent1').value.trim();
-        var response2 = document.getElementById('msgcontent2').value.trim();
-        var response3 = document.getElementById('msgcontent3').value.trim();
-        var response4 = document.getElementById('msgcontent4').value.trim();
-        var response5 = document.getElementById('msgcontent5').value.trim();
-        var response6 = document.getElementById('msgcontent6').value.trim();
+        var response1 = sanitize(document.getElementById('msgcontent1').value.trim());
+        var response2 = sanitize(document.getElementById('msgcontent2').value.trim());
+        var response3 = sanitize(document.getElementById('msgcontent3').value.trim());
+        var response4 = sanitize(document.getElementById('msgcontent4').value.trim());
+        var response5 = sanitize(document.getElementById('msgcontent5').value.trim());
+        var response6 = sanitize(document.getElementById('msgcontent6').value.trim());
         var newMsg = response1 + response2 + response3 + response4 + response5 + response6;
         if (newMsg.length == 0)
Index: /trunk/webapps/common/js/revision_number.dat
===================================================================
--- /trunk/webapps/common/js/revision_number.dat	(revision 434)
+++ /trunk/webapps/common/js/revision_number.dat	(revision 440)
@@ -1,3 +1,3 @@
 
-    var revisionNumber = "433";
+    var revisionNumber = "439";
     
Index: /trunk/webapps/unifiedlog.html
===================================================================
--- /trunk/webapps/unifiedlog.html	(revision 440)
+++ /trunk/webapps/unifiedlog.html	(revision 440)
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+    <title>Unified Logger v</title> 
+<style>
+* {
+  box-sizing: border-box;
+}
+body {
+   background-color: #000033;
+   color: goldenrod
+}
+/* styling for messages */
+#msgs{
+  padding: 15px 10% 15px 10%;
+  font-size: 20px;
+  font-family: monospace;
+  font-weight:lighter;
+  color: white;
+  padding-left: 2%;
+  padding-right: 2%;
+}
+</style>
+</head>
+<body>
+<div class="row">
+    <p id="msgs"></p>
+</div>
+    <script  src="common/js/fileutils.js"></script>
+    <script  src="common/js/revision_number.dat"></script>
+    <script  src="common/js/displayutils.js"></script> 
+   <script>
+// Load the unified log 
+function loadLog()
+{
+    var display = document.getElementById("msgs");
+    display.innerHTML = ""
+    // Asynchronous file read of unified log data
+    loadJSON("dynamicdata/unifiedlog.csv", function(response)
+    {
+        // Format the csv data into an HTML table
+        var allRows = response.split(/\r?\n|\r/);
+        var table = '<table>';
+        // Put the last log entry at the TOP of the table
+        for (var singleRow = allRows.length-1; singleRow > 0; singleRow--)
+        {
+            table += '<tr>';
+            var rowCells = allRows[singleRow].split(',');
+            for (var rowCell = 0; rowCell < rowCells.length; rowCell++)
+            {
+                table += '<td>';
+                table += rowCells[rowCell];
+                table += '</td>';
+            }
+            table += '</tr>';
+        }
+        table += '</table>';
+        // Add the table to the messages div
+        display.innerHTML += table;
+    }
+    );
+}
+// Start
+showRevision();
+loadLog();
+// start an interval timer to refresh the log every 10 seconds
+var displayTimer = setInterval(loadLog, 10000);
+   </script>
+  </body>
+</html>
Index: /trunk/src/python/unifiedlogger/logging_service.py
===================================================================
--- /trunk/src/python/unifiedlogger/logging_service.py	(revision 439)
+++ /trunk/src/python/unifiedlogger/logging_service.py	(revision 439)
@@ -0,0 +1,74 @@
+import cms_watcher, cad_watcher, time, json, ConfigParser
+# Unified Logging Service
+# jdalbey 7/6/2019
+
+outputFilename = "unifiedlog.csv"
+
+# Load the sim time file and extract the seconds */
+def getSimTime():
+    with open ("webapps/dynamicdata/sim_elapsedtime.json", 'r') as myfile:
+        jsonData=myfile.read()
+         
+    seconds = json.loads(jsonData)['elapsedtime']
+    # convert seconds to H:MM:SS
+    m, s = divmod(int(seconds), 60)
+    h, m = divmod(m, 60)
+    return  "%d:%02d:%02d" % (h, m, s)    
+
+def startup():
+    # get path to output file from configuration
+    config = ConfigParser.ConfigParser()
+    config.read('config/logging_service.cfg')
+    logfilepath  = config.get('Paths', 'UnifiedLogPath')
+    
+    # Delete any previously existing output file
+    f = open(logfilepath + outputFilename, "w")
+    f.close()            
+    # List of the available plugin modules 
+    plugins = ["cms_watcher","cad_watcher"]
+    #FOR each plugin LOOP
+    for plugin in plugins:
+        # dynamically load the setup function for this plugin
+        plugmodule = globals()[plugin]
+        setupfunc = getattr(plugmodule, 'setup')
+        #Call setup
+        setupfunc()
+    #END LOOP
+    
+    #DO Forever
+    while True:
+    #    Get simulation time
+        timeStamp = getSimTime()
+    #    Reset Output Buffer
+        output = ""
+        results = []
+    #    FOR each plugin LOOP
+        for plugin in plugins:
+            # dynamically load the setup function for this plugin
+            plugmodule = globals()[plugin]
+            getfunc = getattr(plugmodule, 'getLogEntries')
+            
+    #        Run the plugin process returning new log entries
+            results = getfunc()     
+            #    Append simulation time and the log entries to the Output Buffer
+            for item in results:
+                trimmed_item = item.strip()
+                if len(trimmed_item) > 0:
+                    output += timeStamp + ", " + trimmed_item + "\n"
+    #    END LOOP
+    #    IF the Output Buffer has any contents THEN
+        if len(output) > 0:
+    #       Write (append) Output Buffer to unified log file as CSV
+    #       Assumes fields don't contain commas 
+            print output,
+            f = open(logfilepath + outputFilename, "a")
+            f.write(output)
+            f.close()            
+    #    END IF
+    #    Wait five seconds
+        time.sleep(5)
+
+#END DO
+
+if __name__ == '__main__':
+    startup()
Index: /trunk/src/python/unifiedlogger/wing_project.wpr
===================================================================
--- /trunk/src/python/unifiedlogger/wing_project.wpr	(revision 440)
+++ /trunk/src/python/unifiedlogger/wing_project.wpr	(revision 440)
@@ -0,0 +1,545 @@
+#!wing
+#!version=7.0
+##################################################################
+# Wing project file                                              #
+##################################################################
+[project attributes]
+debug.launch-configs = (2,
+                        {'launch-Dap54cn2qAN7AXZm': ({},
+        {'buildcmd': ('project',
+                      None),
+         'env': ('project',
+                 [u'']),
+         'name': 'launchme',
+         'pyexec': ('project',
+                    u''),
+         'pypath': ('project',
+                    []),
+         'pyrunargs': ('project',
+                       '-u'),
+         'runargs': u'',
+         'rundir': ('custom',
+                    u'/home/jdalbey/Dropbox/TMCrepo/trunk')})})
+proj.directory-list = [{'dirloc': loc('.'),
+                        'excludes': (),
+                        'filter': u'*',
+                        'include_hidden': False,
+                        'recursive': True,
+                        'watch_for_changes': True}]
+proj.file-type = 'normal'
+proj.home-dir = loc('../..')
+proj.launch-config = {loc('__main__.py'): ('custom',
+        (u'',
+         'launch-Dap54cn2qAN7AXZm'))}
+proj.main-file = loc('__main__.py')
+[user attributes]
+debug.show-args-dialog = {loc('__main__.py'): False}
+guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
+                            'windows': [{'name': '0BtsNVOAornvyNAPNOxHAmuDFR'\
+        'v7Dw8l',
+        'size-state': 'maximized',
+        'type': 'dock',
+        'view': {'area': 'tall',
+                 'constraint': None,
+                 'current_pages': [4],
+                 'full-screen': False,
+                 'notebook_display': 'normal',
+                 'notebook_percent': 0.3850187265917603,
+                 'override_title': None,
+                 'pagelist': [('source-assistant',
+                               'tall',
+                               2,
+                               {}),
+                              ('debug-stack',
+                               'tall',
+                               1,
+                               {}),
+                              ('browser',
+                               'tall',
+                               0,
+                               {}),
+                              ('indent',
+                               'tall',
+                               2,
+                               {}),
+                              ('debug-io',
+                               'tall',
+                               0,
+                               {}),
+                              ('project',
+                               'tall',
+                               0,
+                               {'tree-state': {'file-sort-method': 'by name',
+        'list-files-first': False,
+        'tree-states': {'deep': {'expanded-nodes': [(0,),
+        (0,
+         0)],
+                                 'selected-nodes': [(0,
+        0,
+        0)],
+                                 'top-node': (0,)}},
+        'tree-style': 'deep'}})],
+                 'primary_view_state': {'area': 'wide',
+        'constraint': None,
+        'current_pages': [3,
+                          1],
+        'notebook_display': 'hidden',
+        'notebook_percent': 0.0,
+        'override_title': None,
+        'pagelist': [('batch-search',
+                      'wide',
+                      0,
+                      {'fScope': {'fFileSetName': 'All Source Files',
+                                  'fLocation': None,
+                                  'fRecursive': True,
+                                  'fType': 'documentation'},
+                       'fSearchSpec': {'fEndPos': None,
+                                       'fIncludeLinenos': True,
+                                       'fInterpretBackslashes': False,
+                                       'fMatchCase': False,
+                                       'fOmitBinary': True,
+                                       'fRegexFlags': 42,
+                                       'fReplaceText': '',
+                                       'fReverse': False,
+                                       'fSearchText': u'default directory policy',
+                                       'fStartPos': 0,
+                                       'fStyle': 'text',
+                                       'fWholeWords': False,
+                                       'fWrap': True},
+                       'fUIOptions': {'fAutoBackground': True,
+                                      'fFilePrefix': 'short-file',
+                                      'fFindAfterReplace': True,
+                                      'fInSelection': False,
+                                      'fIncremental': True,
+                                      'fReplaceOnDisk': False,
+                                      'fShowFirstMatch': False,
+                                      'fShowLineno': True,
+                                      'fShowReplaceWidgets': False},
+                       'replace-entry-expanded': False,
+                       'search-entry-expanded': False}),
+                     ('interactive-search',
+                      'wide',
+                      0,
+                      {'fScope': {'fFileSetName': 'All Source Files',
+                                  'fLocation': None,
+                                  'fRecursive': True,
+                                  'fType': 'project-files'},
+                       'fSearchSpec': {'fEndPos': None,
+                                       'fIncludeLinenos': True,
+                                       'fInterpretBackslashes': False,
+                                       'fMatchCase': False,
+                                       'fOmitBinary': True,
+                                       'fRegexFlags': 42,
+                                       'fReplaceText': u'lastLineNum',
+                                       'fReverse': False,
+                                       'fSearchText': u'fileLength',
+                                       'fStartPos': 0,
+                                       'fStyle': 'text',
+                                       'fWholeWords': False,
+                                       'fWrap': True},
+                       'fUIOptions': {'fAutoBackground': True,
+                                      'fFilePrefix': 'short-file',
+                                      'fFindAfterReplace': True,
+                                      'fInSelection': False,
+                                      'fIncremental': True,
+                                      'fReplaceOnDisk': False,
+                                      'fShowFirstMatch': False,
+                                      'fShowLineno': True,
+                                      'fShowReplaceWidgets': True}}),
+                     ('debug-data',
+                      'wide',
+                      0,
+                      {}),
+                     ('debug-exceptions',
+                      'wide',
+                      0,
+                      {}),
+                     ('python-shell',
+                      'wide',
+                      2,
+                      {'active-range': (None,
+        -1,
+        -1),
+                       'attrib-starts': [],
+                       'code-line': '',
+                       'first-line': 0L,
+                       'folded-linenos': [],
+                       'history': {},
+                       'launch-id': None,
+                       'sel-line': 3L,
+                       'sel-line-start': 132L,
+                       'selection_end': 132L,
+                       'selection_start': 132L,
+                       'zoom': 0L}),
+                     ('messages',
+                      'wide',
+                      2,
+                      {'current-domain': 0}),
+                     ('os-command',
+                      'wide',
+                      1,
+                      {'last-percent': 0.8,
+                       'toolbox-percent': 1.0,
+                       'toolbox-tree-sel': ''})],
+        'primary_view_state': {'editor_states': ({'bookmarks': ([[loc('cms_watcher.py'),
+        {'attrib-starts': [('main|0|',
+                            85)],
+         'code-line': 'def main():\n',
+         'first-line': 60L,
+         'folded-linenos': [],
+         'sel-line': 85L,
+         'sel-line-start': 3074L,
+         'selection_end': 3085L,
+         'selection_start': 3085L,
+         'zoom': 0L},
+        1562878428.05693],
+        [loc('cad_watcher.py'),
+         {'attrib-starts': [],
+          'code-line': '\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 10L,
+          'sel-line-start': 402L,
+          'selection_end': 402L,
+          'selection_start': 402L,
+          'zoom': 0L},
+         1562878905.96885],
+        [loc('__main__.py'),
+         {'attrib-starts': [('main|0|',
+                             2)],
+          'code-line': '    print "Unified Logging Service starting from ",o'\
+                       's.getcwd()\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 3L,
+          'sel-line-start': 132L,
+          'selection_end': 183L,
+          'selection_start': 183L,
+          'zoom': 0L},
+         1562878936.888077],
+        [loc('../../../../../../../usr/lib/python2.7/ConfigParser.py'),
+         {'attrib-starts': [('ConfigParser|0|',
+                             85),
+                            ('ConfigParser|0|.get|0|',
+                             589)],
+          'code-line': '                raise NoSectionError(section)\n',
+          'first-line': 597L,
+          'folded-linenos': [],
+          'sel-line': 606L,
+          'sel-line-start': 22028L,
+          'selection_end': 22028L,
+          'selection_start': 22028L,
+          'zoom': 0L},
+         1562879070.061342],
+        [loc('../../../../../../../usr/lib/wing-personal7/doc/TOC'),
+         {'displayed-index': 102,
+          'viewer-state': {'history': [-1,
+                                       60,
+                                       -1,
+                                       6,
+                                       1,
+                                       -1,
+                                       102,
+                                       234],
+                           'history-pos': 6,
+                           'index': 102,
+                           'top': 0,
+                           'top-left-pos': 5971}},
+         1562879090.358421],
+        [loc('../../../../../../../usr/lib/wing-personal7/doc/TOC'),
+         {'displayed-index': 234,
+          'viewer-state': {'history': [-1,
+                                       60,
+                                       -1,
+                                       6,
+                                       1,
+                                       -1,
+                                       102,
+                                       234],
+                           'history-pos': 7,
+                           'index': 234,
+                           'top': 0,
+                           'top-left-pos': 20113}},
+         1562879176.767588],
+        [loc('../../../../../../../usr/lib/python2.7/ConfigParser.py'),
+         {'attrib-starts': [('ConfigParser|0|',
+                             85),
+                            ('ConfigParser|0|.get|0|',
+                             589)],
+          'code-line': '                raise NoSectionError(section)\n',
+          'first-line': 597L,
+          'folded-linenos': [],
+          'sel-line': 606L,
+          'sel-line-start': 22028L,
+          'selection_end': 22028L,
+          'selection_start': 22028L,
+          'zoom': 0L},
+         1562879399.049104],
+        [loc('../../../../../../../usr/lib/wing-personal7/doc/TOC'),
+         {'displayed-index': 234,
+          'viewer-state': {'history': [-1,
+                                       60,
+                                       -1,
+                                       6,
+                                       1,
+                                       -1,
+                                       102,
+                                       234],
+                           'history-pos': 7,
+                           'index': 234,
+                           'top': 0,
+                           'top-left-pos': 101}},
+         1562879632.459482],
+        [loc('../../../../../../../usr/lib/python2.7/ConfigParser.py'),
+         {'attrib-starts': [('ConfigParser|0|',
+                             85),
+                            ('ConfigParser|0|.get|0|',
+                             589)],
+          'code-line': '                raise NoSectionError(section)\n',
+          'first-line': 597L,
+          'folded-linenos': [],
+          'sel-line': 606L,
+          'sel-line-start': 22028L,
+          'selection_end': 22028L,
+          'selection_start': 22028L,
+          'zoom': 0L},
+         1562879640.602333],
+        [loc('logging_service.py'),
+         {'attrib-starts': [('startup|0|',
+                             16)],
+          'code-line': '        timeStamp = getSimTime()\n',
+          'first-line': 18L,
+          'folded-linenos': [],
+          'sel-line': 41L,
+          'sel-line-start': 1347L,
+          'selection_end': 1378L,
+          'selection_start': 1378L,
+          'zoom': 0L},
+         1562879647.165641],
+        [loc('__main__.py'),
+         {'attrib-starts': [('main|0|',
+                             2)],
+          'code-line': '    print "Unified Logging Service starting from ",o'\
+                       's.getcwd()\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 3L,
+          'sel-line-start': 132L,
+          'selection_end': 183L,
+          'selection_start': 183L,
+          'zoom': 0L},
+         1562879668.696399],
+        [loc('logging_service.py'),
+         {'attrib-starts': [('startup|0|',
+                             16)],
+          'code-line': '        timeStamp = getSimTime()\n',
+          'first-line': 18L,
+          'folded-linenos': [],
+          'sel-line': 41L,
+          'sel-line-start': 1347L,
+          'selection_end': 1378L,
+          'selection_start': 1378L,
+          'zoom': 0L},
+         1562880191.70851],
+        [loc('cms_watcher.py'),
+         {'attrib-starts': [('extractMessages|0|',
+                             45)],
+          'code-line': '\n',
+          'first-line': 45L,
+          'folded-linenos': [],
+          'sel-line': 56L,
+          'sel-line-start': 1978L,
+          'selection_end': 1978L,
+          'selection_start': 1978L,
+          'zoom': 0L},
+         1562880439.712792],
+        [loc('logging_service.py'),
+         {'attrib-starts': [('startup|0|',
+                             16)],
+          'code-line': '        timeStamp = getSimTime()\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 41L,
+          'sel-line-start': 1347L,
+          'selection_end': 1378L,
+          'selection_start': 1378L,
+          'zoom': 0L},
+         1562880463.667528],
+        [loc('cms_watcher.py'),
+         {'attrib-starts': [('getLogEntries|0|',
+                             61)],
+          'code-line': "               results.append(\"CMS Updated., \" + l"\
+                       "ocationMap[idList[idx]] + \", '\" + currList[idx] +"\
+                       "\"'\")\n",
+          'first-line': 57L,
+          'folded-linenos': [],
+          'sel-line': 78L,
+          'sel-line-start': 2846L,
+          'selection_end': 2924L,
+          'selection_start': 2924L,
+          'zoom': 0L},
+         1562880625.962149],
+        [loc('cad_watcher.py'),
+         {'attrib-starts': [('setup|0|',
+                             31)],
+          'code-line': '    return\n',
+          'first-line': 24L,
+          'folded-linenos': [],
+          'sel-line': 33L,
+          'sel-line-start': 888L,
+          'selection_end': 898L,
+          'selection_start': 898L,
+          'zoom': 0L},
+         1562882880.377423],
+        [loc('cms_watcher.py'),
+         {'attrib-starts': [('extractMessages|0|',
+                             45)],
+          'code-line': "                    msgList[idx]['cms']['message']['"\
+                       "phase2']['Line2'] + ':' +\n",
+          'first-line': 39L,
+          'folded-linenos': [],
+          'sel-line': 54L,
+          'sel-line-start': 1829L,
+          'selection_end': 1903L,
+          'selection_start': 1903L,
+          'zoom': 0L},
+         1562883842.916941],
+        [loc('logging_service.py'),
+         {'attrib-starts': [('startup|0|',
+                             17)],
+          'code-line': '            f = open(logfilepath + outputFilename, "'\
+                       'a")\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 63L,
+          'sel-line-start': 2214L,
+          'selection_end': 2263L,
+          'selection_start': 2263L,
+          'zoom': 0L},
+         1562887416.91585],
+        [loc('__main__.py'),
+         {'attrib-starts': [('main|0|',
+                             2)],
+          'code-line': '    print "Unified Logging Service starting from ",o'\
+                       's.getcwd()\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 3L,
+          'sel-line-start': 132L,
+          'selection_end': 183L,
+          'selection_start': 183L,
+          'zoom': 0L},
+         1562892803.750792],
+        [loc('cms_watcher.py'),
+         {'attrib-starts': [('isEmpty|0|',
+                             12)],
+          'code-line': '     return cmsitem == ":::::"\n',
+          'first-line': 0L,
+          'folded-linenos': [],
+          'sel-line': 13L,
+          'sel-line-start': 324L,
+          'selection_end': 353L,
+          'selection_start': 353L,
+          'zoom': 0L},
+         1562893146.713132]],
+        20),
+        'current-loc': loc('__main__.py'),
+        'editor-state-list': [(loc('cad_watcher.py'),
+                               {'attrib-starts': [('setup|0|',
+        31)],
+                                'code-line': '    return\n',
+                                'first-line': 24L,
+                                'folded-linenos': [],
+                                'sel-line': 33L,
+                                'sel-line-start': 888L,
+                                'selection_end': 898L,
+                                'selection_start': 898L,
+                                'zoom': 0L}),
+                              (loc('cms_watcher.py'),
+                               {'attrib-starts': [('isEmpty|0|',
+        12)],
+                                'code-line': '     return cmsitem == ":::::"'\
+        '\n',
+                                'first-line': 0L,
+                                'folded-linenos': [],
+                                'sel-line': 13L,
+                                'sel-line-start': 324L,
+                                'selection_end': 353L,
+                                'selection_start': 353L,
+                                'zoom': 0L}),
+                              (loc('logging_service.py'),
+                               {'attrib-starts': [('startup|0|',
+        17)],
+                                'code-line': '            f = open(logfilepa'\
+        'th + outputFilename, "a")\n',
+                                'first-line': 0L,
+                                'folded-linenos': [],
+                                'sel-line': 63L,
+                                'sel-line-start': 2214L,
+                                'selection_end': 2263L,
+                                'selection_start': 2263L,
+                                'zoom': 0L}),
+                              (loc('__main__.py'),
+                               {'attrib-starts': [('main|0|',
+        2)],
+                                'code-line': '    print "Unified Logging Ser'\
+        'vice starting from ",os.getcwd()\n',
+                                'first-line': 0L,
+                                'folded-linenos': [],
+                                'sel-line': 3L,
+                                'sel-line-start': 132L,
+                                'selection_end': 183L,
+                                'selection_start': 183L,
+                                'zoom': 0L})],
+        'has-focus': True,
+        'locked': False},
+        [loc('cad_watcher.py'),
+         loc('cms_watcher.py'),
+         loc('logging_service.py'),
+         loc('__main__.py')]),
+                               'open_files': [u'cad_watcher.py',
+        u'logging_service.py',
+        u'cms_watcher.py',
+        u'__main__.py']},
+        'saved_notebook_display': None,
+        'split_percents': {0: 0.5},
+        'splits': 2,
+        'tab_location': 'top',
+        'traversal_pos': ((0,
+                           3),
+                          1562879301.119771),
+        'user_data': {}},
+                 'saved_notebook_display': None,
+                 'split_percents': {},
+                 'splits': 1,
+                 'tab_location': 'left',
+                 'traversal_pos': ((0,
+                                    4),
+                                   1562892833.554952),
+                 'user_data': {}},
+        'window-alloc': (29,
+                         0,
+                         1197,
+                         722)}]}
+guimgr.recent-documents = [loc('__main__.py'),
+                           loc('cms_watcher.py'),
+                           loc('logging_service.py'),
+                           loc('cad_watcher.py')]
+guimgr.visual-state = {loc('../../../../../../../usr/lib/python2.7/ConfigParser.py'): {'a'\
+        'ttrib-starts': [('ConfigParser|0|',
+                          85),
+                         ('ConfigParser|0|.get|0|',
+                          589)],
+        'code-line': '                raise NoSectionError(section)\n',
+        'first-line': 597L,
+        'folded-linenos': [],
+        'sel-line': 606L,
+        'sel-line-start': 22028L,
+        'selection_end': 22028L,
+        'selection_start': 22028L,
+        'zoom': 0L}}
+proj.build-cmd = {None: ('default',
+                         None)}
+proj.env-vars = {None: ('default',
+                        [u''])}
+search.search-history = [u'default directory policy']
Index: /trunk/src/python/unifiedlogger/cms_watcher.py
===================================================================
--- /trunk/src/python/unifiedlogger/cms_watcher.py	(revision 440)
+++ /trunk/src/python/unifiedlogger/cms_watcher.py	(revision 440)
@@ -0,0 +1,103 @@
+import json, time
+from copy import deepcopy
+
+# CMS Message Watcher
+# Look for changes in the CMS message file
+# jdalbey  7/6/2019
+idList = []    # list of CMS ID's
+prevList = []  # previous messages
+currList = []  # current messages
+locationMap = {} # map of CMS ID's to locations
+
+# Utility functions
+def isEmpty(cmsitem):
+     return cmsitem == ":::::"
+def isFull(cmsitem):
+     return not isEmpty(cmsitem)
+
+# Read the cms message file
+def readFile():     
+     with open ("webapps/dynamicdata/cms_messages.json",'r') as myfile:
+          jsonData=myfile.read()
+ 
+     return json.loads(jsonData)['data']
+
+# Read the static file of cms locations and create a lookup map
+def loadLocations():
+     with open ("webapps/cptms/data_layers/cms_locations_D12.gjson",'r') as myfile:
+          jsonData=myfile.read()
+ 
+     list = json.loads(jsonData)['features']
+     # add each item to a lookup map
+     for item in list:
+          locationMap [item['id']] = item['properties']['location'] + " " + item['properties']['street']
+     
+# Setup the ID list and initialize the previous messages to empty
+def initialize():
+     loadLocations()
+     msgList = readFile()
+     global prevList, idList
+     for idx in range(0,len(msgList)):
+          idList.append(msgList[idx]['cms']['index'])
+          prevList.append(":::::")
+     return msgList
+
+# Extract the current messages into a list
+def extractMessages(msgList):
+     global currList
+     currList = []
+     for idx in range(0,len(msgList)):
+          currList.append(
+                    msgList[idx]['cms']['message']['phase1']['Line1'] + ':' + 
+                    msgList[idx]['cms']['message']['phase1']['Line2'] + ':' +
+                    msgList[idx]['cms']['message']['phase1']['Line3'] + ':' +
+                    msgList[idx]['cms']['message']['phase2']['Line1'] + ':' +
+                    msgList[idx]['cms']['message']['phase2']['Line2'] + ':' +
+                    msgList[idx]['cms']['message']['phase2']['Line3'])
+
+def setup():
+     extractMessages(initialize())
+
+# compare previous messages to current messages to look for changes
+def getLogEntries():
+     global prevList, currList
+     msgList = readFile()
+     extractMessages(msgList)
+     size = len(currList)
+     
+     results = []
+     # Consider each CMS message
+     for idx in range(0,size):
+          # Is a new message activated?
+          if isEmpty(prevList[idx]) and isFull(currList[idx]):
+               results.append("CMS Activated.," + locationMap[idList[idx]] + ", '" + currList[idx] +"'")
+          # Is an existing message turned off?
+          if isEmpty(currList[idx]) and isFull(prevList[idx]):
+               results.append("CMS Deactivated., " + locationMap[idList[idx]])
+          # Did a message change?
+          if isFull(currList[idx]) and isFull(prevList[idx]) and currList[idx] != prevList[idx]:
+               results.append("CMS Updated., " + locationMap[idList[idx]] + ", '" + currList[idx] +"'")
+
+     # Save the current list as previous
+     prevList = deepcopy(currList)
+     return results
+
+# Local main for unit testing
+def main():
+     global currList
+     setup()
+     # Loop Forever
+     while True:
+          # Look for changed messages
+          answer = getLogEntries()
+          # Output results
+          for item in answer:
+               print item
+          # wait 
+          time.sleep(5)
+          # Get the current messages
+          extractMessages(readFile())
+
+if __name__ == "__main__":
+     main()
+     
Index: /trunk/src/python/unifiedlogger/__main__.py
===================================================================
--- /trunk/src/python/unifiedlogger/__main__.py	(revision 439)
+++ /trunk/src/python/unifiedlogger/__main__.py	(revision 439)
@@ -0,0 +1,8 @@
+import logging_service, os
+# This main module is provided so the application can be conveniently bundled for deployment
+def main():
+    print "Unified Logging Service starting from ",os.getcwd()
+    logging_service.startup()
+
+if __name__ == '__main__':
+    main()
Index: /trunk/src/python/unifiedlogger/cad_watcher.py
===================================================================
--- /trunk/src/python/unifiedlogger/cad_watcher.py	(revision 439)
+++ /trunk/src/python/unifiedlogger/cad_watcher.py	(revision 439)
@@ -0,0 +1,59 @@
+import json, time
+from copy import deepcopy
+
+# CAD comment log Watcher
+# Look for changes in the CAD comment log.
+# The CADserver will append new comments to the log as they arrive 
+# from clients.  We only need to keep track of the log length in order to
+# determine if a new comment has been added.  We will output the 
+# new messages that arrived during the last wait interval. 
+# jdalbey  7/6/2019
+
+lastLineNum = 0
+
+# Utility functions
+def isEmpty(cmsitem):
+    return cmsitem == ",,,,,"
+def isFull(cmsitem):
+    return not isEmpty(cmsitem)
+
+# Read the cad comments log 
+def readFile():
+    lines = []
+    try:
+        text_file = open("CADcomments.log", "r")
+    except IOError:
+        print "Error: missing CADcomments.log file."
+    else:
+        lines = text_file.read().split('\n')
+        text_file.close()    
+    return lines
+    
+def setup():
+    # nothing needed for setup
+    return
+
+# Retrieve new messages from CAD comment log 
+def getLogEntries():
+    global lastLineNum
+    msgList = readFile()
+    currList = []
+    currList = msgList[lastLineNum:]    # new items since last file read
+    lastLineNum = len(msgList)-1
+    return currList
+
+# Local main for unit testing
+def main():
+    setup()
+    # Loop Forever, checking every five seconds
+    while True:
+        # Look for new messages
+        answer = getLogEntries()
+        # Output results
+        for item in answer:
+            print item
+        # wait 
+        time.sleep(5)
+
+if __name__ == "__main__":
+    main()
Index: /trunk/src/python/demo/config/logging_service.cfg
===================================================================
--- /trunk/src/python/demo/config/logging_service.cfg	(revision 440)
+++ /trunk/src/python/demo/config/logging_service.cfg	(revision 440)
@@ -0,0 +1,2 @@
+[Paths]
+UnifiedLogPath = ../dynamicdata/
Index: /trunk/src/python/demo/__main__.py
===================================================================
--- /trunk/src/python/demo/__main__.py	(revision 440)
+++ /trunk/src/python/demo/__main__.py	(revision 440)
@@ -0,0 +1,15 @@
+import os, ConfigParser
+
+# This main module is provided so the application can be conveniently bundled for deployment
+def main():
+    print os.getcwd()
+    print "Hello Demo World"
+
+    # get path to output file from configuration
+    config = ConfigParser.ConfigParser()
+    config.read('config/logging_service.cfg')
+    logfilepath  = config.get('Paths', 'UnifiedLogPath')
+    print "logfile path is: ",logfilepath
+
+if __name__ == '__main__':
+    main()
Index: /trunk/src/tmcsim/cadsimulator/Coordinator.java
===================================================================
--- /trunk/src/tmcsim/cadsimulator/Coordinator.java	(revision 435)
+++ /trunk/src/tmcsim/cadsimulator/Coordinator.java	(revision 440)
@@ -758,5 +758,5 @@
                 if (initials.length() > 0)
                 {//CAD Log Entry, Incident #187, Sharon: REQUEST EXTRA ANCHOVIES
-                    output.append("CAD log entry, ");
+                    output.append("CAD log, ");
                     output.append("Incident #" + incident.logNum + ", ");
                     output.append(initials + ": ");
Index: /trunk/src/tmcsim/application.properties
===================================================================
--- /trunk/src/tmcsim/application.properties	(revision 435)
+++ /trunk/src/tmcsim/application.properties	(revision 440)
@@ -1,5 +1,5 @@
-#Wed, 10 Jul 2019 15:20:20 -0700
+#Thu, 11 Jul 2019 17:51:05 -0700
 
-Application.revision=434
+Application.revision=439
 
 Application.buildnumber=146
