An access controller for man doors at our facility. It receives Wiegand signals from a keypad/card reader and activates a relay to open the door. Access codes are stored in EEPROM. The active code list is updated from TFTP on a local server.

Dependencies:   24LCxx_I2C CardReader USBHOST

Revision:
0:a56239ae90c2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ConfigurationManager.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,386 @@
+
+#include "ConfigurationManager.h"
+
+void ConfigurationManager::remove_all_chars(char* str, char c) {
+    char *pr = str, *pw = str;
+    while (*pr) {
+        *pw = *pr++;
+        pw += (*pw != c);
+    }
+    *pw = '\0';
+}
+
+bool ConfigurationManager::isValidIpString(char* ip)
+{
+    bool isValid = false;
+    int strLength = strlen(ip);
+    int numPeriods = 0;
+    
+    // Verify that the length is within bounds.
+    if (strLength >= IP_STRING_MIN_LENGTH && strLength <= IP_STRING_MAX_LENGTH)
+    {
+        // Verify that there are three periods in the string.
+        for (int charIndex = 0; charIndex < strLength; charIndex++)
+        {
+            numPeriods += (ip[charIndex] == '.');
+        }
+        
+        isValid = (numPeriods == 3);
+    }
+    
+    return isValid;
+}
+
+int ConfigurationManager::parse_tftp_config(char* data)
+{
+    int dataLength = strlen(data);
+    int paramStart = 0;
+    int paramNameLength = 0;
+    int paramValueLength = 0;
+    char paramName[CONFIG_PARAM_NAME_MAX_LENGTH + 1] = "";
+    char paramValue[CONFIG_PARAM_VALUE_MAX_LENGTH + 1] = "";
+    bool paramComplete = false;
+    
+    // Get the parameter name
+    for (int charIndex = 0; charIndex <= dataLength; charIndex++)
+    {
+        // Check for end of the parameter name.
+        if (data[charIndex] == '=')
+        {
+            paramNameLength = charIndex - paramStart;
+        }
+        
+        // Check for the end of the parameter value.
+        if (data[charIndex] == '\r' || data[charIndex] == '\n' || data[charIndex] == ' ' || charIndex == dataLength)
+        {
+            int paramValueStart = (paramStart + paramNameLength + 1);
+            paramValueLength = charIndex - paramValueStart;
+            
+            // Handle oversized input strings.
+            if (paramNameLength > CONFIG_PARAM_NAME_MAX_LENGTH || paramValueLength > CONFIG_PARAM_VALUE_MAX_LENGTH)
+            {
+                paramComplete = true;
+            }
+            
+            if (!paramComplete && paramNameLength > 0 && paramValueLength > 0)
+            {
+                // Get the name and value strings.
+                memcpy(paramName, data + paramStart, paramNameLength);
+                paramName[paramNameLength] = 0;
+                memcpy(paramValue, data + paramValueStart, paramValueLength);
+                paramValue[paramValueLength] = 0;
+                
+                printf("  %s=%s\n", paramName, paramValue);
+                
+                // Store the parameter value.
+                if (!strcmp(paramName, configName_enableAccess))
+                {
+                    config_enableAccess = (paramValue[0] == '1');
+                }
+                else if (!strcmp(paramName, configName_ipAddress) && isValidIpString(paramValue))
+                {
+                    strcpy(config_ipAddress, paramValue);
+                }
+                else if (!strcmp(paramName, configName_netMask) && isValidIpString(paramValue))
+                {
+                    strcpy(config_netMask, paramValue);
+                }
+                else if (!strcmp(paramName, configName_gateway) && isValidIpString(paramValue))
+                {
+                    strcpy(config_gateway, paramValue);
+                }
+                else if (!strcmp(paramName, configName_codeFileName) && strlen(paramValue) <= CONFIG_PARAM_VALUE_MAX_LENGTH)
+                {
+                    strcpy(config_codeFileName, paramValue);
+                }
+                else if (!strcmp(paramName, configName_ntpServer) && isValidIpString(paramValue))
+                {
+                    strcpy(config_ntpServer, paramValue);
+                }
+                else if (!strcmp(paramName, configName_usbBaud))
+                {
+                    config_usbBaud = atoi(paramValue);
+                }
+            }
+            
+            paramComplete = true;
+        }
+        
+        // Reset for the next parameter.
+        if (data[charIndex] == '\n')
+        {
+            paramStart = charIndex + 1;
+            paramNameLength = 0;
+            paramValueLength = 0;
+            paramName[0] = 0;
+            paramValue[0] = 0;
+            paramComplete = false;
+        }
+    }
+    
+    return 0;
+}
+
+int ConfigurationManager::parse_tftp_codes(char* tftp_data)
+{
+    unsigned short codeList[CODE_TABLE_SIZE / 2] = {0};
+    unsigned int dataLength = strlen(tftp_data);
+    int codeStart = 0;
+    int codeLength = 0;
+    int codeIndex = 0;
+    char codeString[ACCESS_CODE_MAX_LENGTH + 1] = "";
+    bool codeComplete = true;
+    
+    // Get the parameter name
+    for (int charIndex = 0; charIndex <= dataLength; charIndex++)
+    {
+        if (tftp_data[charIndex] >= '0' && tftp_data[charIndex] <= '9')
+        {
+            if (codeComplete)
+            {
+                codeStart = charIndex;
+                codeLength = 0;
+            }
+            
+            codeLength += 1;
+            codeComplete = false;
+        }
+        else // not a number character
+        {
+            codeComplete = true;
+            
+            // Ensure that the code is of the correct size.
+            if (codeIndex < (CODE_TABLE_SIZE / 2) && codeLength >= ACCESS_CODE_MIN_LENGTH && codeLength <= ACCESS_CODE_MAX_LENGTH)
+            {
+                memcpy(codeString, tftp_data + codeStart, codeLength);
+                codeString[codeLength] = 0;
+                unsigned short codeValue = atoi(codeString);
+                
+                // Store the code in a temporary list to use when updating the EEPROM.
+                codeList[codeIndex] = codeValue;
+                
+                // Format and print the list of access codes with multiple codes per line.
+                //printf(ACCESS_CODE_PRINT_FORMAT, codeValue);
+                codeIndex++;
+                
+                // New line of access codes
+                if (codeIndex % ACCESS_CODE_NUM_COLS == 0)
+                {
+                    //printf("\n");
+                }
+            }
+        }
+    }
+    
+    printf("Downloaded %d codes.\n\n", codeIndex);
+    
+    // Update the EEPROM with the current network list of codes.
+    mCodeMem->SyncAccessCodes(codeList, codeIndex);
+    
+    return 0;
+}
+
+// Set the IP address options from the configuration parameters and connect to the network.
+int ConfigurationManager::connectEthernet(char* mac_addr, char* ip_addr, const bool withDhcp, const bool printStats)
+{
+    int connectResult;
+    
+    mEth->set_dhcp(withDhcp);
+    
+    // Reconfigure the network for a static address if specified.
+    if (!withDhcp && isValidIpString(config_ipAddress) && isValidIpString(config_netMask) && (strlen(config_gateway) == 0 || isValidIpString(config_gateway)))
+    {
+        mEth->set_network(config_ipAddress, config_netMask, config_gateway);
+    }
+    
+    // Attempt network connection
+    connectResult = mEth->connect();
+    
+    if (connectResult >= 0)
+    {
+        strcpy(mac_addr, mEth->get_mac_address());
+        strcpy(ip_addr, mEth->get_ip_address());
+        
+        if (printStats)
+        {
+            printf("Network Connected:\n");
+            printf("  mac:  %s\n", mac_addr);
+            printf("  ip:   %s\n", ip_addr);
+            printf("  mask: %s\n", mEth->get_netmask());
+            printf("  gate: %s\n", mEth->get_gateway());
+        }
+    }
+    else
+    {
+        printf("Failed to connect to the network. %d\n", connectResult);
+    }
+    
+    return connectResult;
+}
+
+void ConfigurationManager::updateClock()
+{
+    // Set the real time clock using NTP
+    printf("\nSending NTP query to %s...\n", config_ntpServer);
+    int ntp_result = _ntp.setTime(mEth, config_ntpServer);
+    if (ntp_result < 0)
+    {
+        printf("Failed to send.\n");
+    }
+    
+    lastUpdatedTimeSeconds = time(NULL);
+    return;
+}
+
+void ConfigurationManager::setupNetwork()
+{
+    printf("\nEstablishing DHCP network connection...\n");
+    
+    // Ethernet Setup: Connect to the netowrk using DHCP
+    _tftp = new TFTPClient(mEth);
+    int connectResult = connectEthernet(_mac_addr, _ip_addr);
+    if (connectResult >= 0)
+    {
+        // Reconfigure the network settings for a temporary static IP address
+        // This is required to free port 68 for the DHCP option query in the next step.
+        strcpy(config_ipAddress, mEth->get_ip_address());
+        strcpy(config_netMask, mEth->get_netmask());
+        strcpy(config_gateway, mEth->get_gateway());
+        mEth->disconnect();
+        
+        connectResult = connectEthernet(_mac_addr, _ip_addr, 0, 1);
+        
+        // Get the IP address of the TFTP server via DHCP offer message.
+        printf("\nGetting the TFTP server address via DHCP...\n");
+        DHCPOptions ops(mEth);
+        int optionResult = ops.getOptions(config_tftpServer, _mac_addr);
+        
+        if (optionResult >= 0)
+        {
+            //updateConfiguration();
+        }
+        else
+        {
+            printf("Failed to get options from DHCP: %d\n", optionResult);
+        }
+    }
+    else if (connectResult == NSAPI_ERROR_DHCP_FAILURE)
+    {
+        // Retry DHCP until it works.
+        // If the error is not due to DHCP, then continue without network features.
+        printf("Resetting...\n");
+        wait(RESET_PAUSE_SECONDS);
+        NVIC_SystemReset();
+    }
+}
+
+void ConfigurationManager::update()
+{
+    // Periodically update the controller configuration from TFTP.
+    if ((time(NULL) - lastUpdatedConfigSeconds) > CONFIG_UPDATE_INTERVAL_SECS)
+    {
+        mUpdateStep = UPDATE_STEP_INITIATE;
+        lastUpdatedConfigSeconds = time(NULL);
+    }
+    
+    if (mUpdateStep == UPDATE_STEP_INITIATE)
+    {
+        // Build TFTP filename from the MAC address.
+        char tftp_filename[MAC_STRING_LENGTH + 1] = "";
+        strcpy(tftp_filename, _mac_addr);
+        remove_all_chars(tftp_filename, ':');
+        strcat(tftp_filename, ".cnf");
+        printf("Getting config file %s from %s...\n", tftp_filename, config_tftpServer);
+        
+        int tftpResult = _tftp->readFile(_tftp_data, config_tftpServer, tftp_filename);
+        if (tftpResult < 0)
+        {
+            printf("  TFTP config file request failed: %d\n", tftpResult);
+        }
+        else
+        {
+            mUpdateStep = UPDATE_STEP_CONFIG_FILE_REQUEST_SENT;
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CONFIG_FILE_REQUEST_SENT)
+    {
+        // Wait for the TFTP response from the server.
+        int tftp_result = _tftp->update();
+        if (tftp_result == 1)
+        {
+            printf("TFTP Success.\n\n");
+            mUpdateStep = UPDATE_STEP_CONFIG_FILE_RECEIVED;
+        }
+        else
+        {
+            printf("  Failed to retrieve configuration file: %d\n", tftp_result);
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CONFIG_FILE_RECEIVED) // retrieve file succeeded
+    {
+        int currentUsbBaud = config_usbBaud;
+        
+        parse_tftp_config(_tftp_data);
+        
+        if (config_usbBaud > 0 && config_usbBaud != currentUsbBaud)
+        {
+            printf("\nSetting USB Serial Baud: %d\n                     \n", config_usbBaud); // Leave extra space because setting baud rate deletes some characters.
+            //usbUART.baud(config_usbBaud);
+        }
+        
+        printf("\nConfiguring static network connection...\n");
+        mEth->disconnect();
+        connectEthernet(_mac_addr, _ip_addr, 0, 1);
+        
+        printf("\nGetting code list file %s from %s...\n", config_codeFileName, config_tftpServer);
+        // Get list of access codes from the specified file on the TFTP server.
+        int tftpResult = _tftp->readFile(_tftp_data, config_tftpServer, config_codeFileName);
+        if (tftpResult < 0)
+        {
+            printf("Failed to send config file request.\n");
+        }
+        else
+        {
+            mUpdateStep = UPDATE_STEP_CODES_FILE_REQUEST_SENT;
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CODES_FILE_REQUEST_SENT)
+    {
+        // Wait for the TFTP response from the server.
+        int tftp_result = _tftp->update();
+        if (tftp_result == 1)
+        {
+            printf("TFTP Success.\n\n");
+            mUpdateStep = UPDATE_STEP_CODES_FILE_RECEIVED;
+        }
+        else
+        {
+            printf("  Failed to retrieve code list: %d\n", tftp_result);
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CODES_FILE_RECEIVED)
+    {
+        parse_tftp_codes(_tftp_data);
+        mUpdateStep = UPDATE_STEP_IDLE;
+    }
+    
+    
+    
+    // Periodically update the RTC via NTP.
+    if ((time(NULL) - lastUpdatedTimeSeconds) > TIME_UPDATE_INTERVAL_SECS)
+    {
+        updateClock();
+    }
+    
+    // Check the status of any ongoing NTP updates.
+    int ntp_result = _ntp.update();
+    if (ntp_result < 0)
+    {
+        printf("NTP query failed.\n\n");
+    }
+    else if (ntp_result > 0)
+    {
+        unsigned int ctTime = time(NULL);
+        printf("Time is now (UTC): %s\n\n", ctime(&ctTime));
+    }
+}