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

ConfigurationManager.cpp

Committer:
acesrobertm
Date:
2017-09-25
Revision:
0:a56239ae90c2

File content as of revision 0:a56239ae90c2:


#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));
    }
}