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
Diff: ConfigurationManager.cpp
- 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)); + } +}