C Application: Choosing an Electrical Plan
People learning to program often struggle with how to decompose a problem into the steps necessary to write a program to solve that problem. This is one of a series of posts in which I take a problem and go through my decision-making process that leads to a program.
Background: In the spring of 2020, it was time to choose a new electrical plan and with my current provider I basically had two options. My home has a smart meter, so I am able to download my electrical usage in increments of 15 minutes. This allows me to apply each plan to actual data to see what the costs would have been.
Also relevant is that prior to April, 2020 I was going to work during the day and therefore wasn’t using as much electricity during the daytime.
Problem: Write a program to choose the cheapest electrical plan based on historical data. I assume that you understand statements, conditionals, loops, arrays, functions, strings, pointers, and file I/O.
You can find a video with more details at https://www.youtube.com/watch?v=rv0DwcdAgPM .
Design: Let’s look at the plans. This gives me an idea of what the relevant values are:
Plan 1 charges different rates for daytime and nightime. They are
daytime: 8am <= time < 8pm $0.088008/kWh
nighttime: 8pm <= time < 8am $0.078008/kWh
delivery cost: $3.42 + $0.035448/kWh
Plan 2 charges the same rate each hour of the day. It’s cost is calculated as
any time: $0.078341/kWh
delivery cost: $3.42 + $0.035448/kWh
The delivery costs are the same for both plans, so I could leave them out since I simply want to know which cost is cheapest. However, I will include them in case a comparison of future plans has them as different costs.
I can see that both plans determine my costs from the the kWh (i.e., kilowatt-hour) used for a time period, but that Plan 1 uses a different rate depending on the time of day. Therefore, I need to know the time of day and the kWh used.
Next, I want to know what I am working with with regard to the data, so I downloaded historical electrical usage for the period of January 1, 2020 to May 31, 2020; the data is in CSV format. Here are the first few lines (with my actual meter number changed):
ESIID,USAGE_DATE,REVISION_DATE,USAGE_START_TIME,USAGE_END_TIME,USAGE_KWH,ESTIMATED_ACTUAL,CONSUMPTION_GENERATION
123456,01/01/2020,01/03/2020 07:51:37,00:00,00:15,.043,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,00:15,00:30,.042,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,00:30,00:45,.064,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,00:45,01:00,.043,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,01:00,01:15,.043,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,01:15,01:30,.043,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,01:30,01:45,.042,A,Consumption
123456,01/01/2020,01/03/2020 07:51:37,01:45,02:00,.052,A,Consumption
The first line is a header line that tells me what the other lines represent, which I will need to deal with when processing the file in my program. I can see that the the columns relevant to my needs are
- column 2: USAGE_DATE — this is the day of the year (necessary only if I want to restrict my comparison to a particular time of the year).
- column 4: USAGE_START_TIME — this is the start of a 15-minute period for a given date
- column 6: USAGE_KWH — this is the amount of electricity used
Why do I care about USAGE_DATE? I ended my employment on May 31, 2020 and am (at the time of this writing) at home during the daytime. Since I am using historical data to predict future costs, I chose to use the data from the month of May since it also reflects me being home during the daytime.
I think I know what to do now: for the month of May, 2020, get the electrical usage for each 15-minute time period and use it to calculate the cost if on Plan 1 and the cost if on Plan 2. This leads to this pseudocode:
/* open file
for each line of file
get date, time, kwh
if date in range
if time is day
update costs for plan 1 using day cost
else
update costs for plan 1 using night cost
update costs for plan 2
close file */
Final Program
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int checkDate(char buffer[], char* m, char t[], double* kwh) ;
double produceCostPlan1(char t[], double kwh) ;
double produceCostPlan2(double kwh) ;
int main(void) {
FILE* fp;
char* filename = "IntervalData.csv";
char buffer[120];
char* month = "05"; /* use May */
char time[6];
double kwh;
int dateMatch;
double costPlan1 = 3.42; /* base delivery charge */
double costPlan2 = 3.42;
if( (fp = fopen(filename, "r")) == NULL ) {
printf("unable to open %s\n", filename);
exit(1);
}
fgets(buffer, sizeof(buffer), fp);
while( fgets(buffer, sizeof(buffer), fp) != NULL ) {
dateMatch = checkDate(buffer, month, time, &kwh);
if(dateMatch) {
costPlan1 += produceCostPlan1(time, kwh) ;
costPlan2 += produceCostPlan2(kwh) ;
}
}
fclose(fp);
printf("cost plan 1 = $%.2f\n", costPlan1);
printf("cost plan 2 = $%.2f\n", costPlan2);
}
int checkDate(char buffer[], char* m, char t[], double* kwh) {
/*
ESIID,USAGE_DATE,REVISION_DATE,USAGE_START_TIME,USAGE_END_TIME,USAGE_KWH,ESTIMATED_ACTUAL,CONSUMPTION_GENERATION
123456,01/01/2020,01/03/2020 07:51:37,00:00,00:15,.043,A,Consumption
ESIID : 123456
USAGE_DATE : 01/01/2020
REVISION_DATE : 01/03/2020 07:51:37
USAGE_START_TIME : 00:00
USAGE_END_TIME : 00:15
USAGE_KWH : .043
ESTIMATED_ACTUAL : A
CONSUMPTION_GENERATION : Consumption
*/
char *token, *del = ",";
char dateField[20];
token = strtok(buffer, del); /* meter ID, throw away */
token = strtok(NULL, del); /* date */
strcpy(dateField, token);
token = strtok(NULL, del); /* revision date, throw away */
token = strtok(NULL, del); /* start time */
strcpy(t, token);
token = strtok(NULL, del); /* end time, throw away */
*kwh = atof(strtok(NULL, del)); /* kwh time */
/* tokenize date field */
token = strtok(dateField, "/");
if( strcmp(token, m) == 0 )
return 1; /* True, month matches */
else
return 0;
}
double produceCostPlan1(char t[], double kwh){
/*
plan 1: nights and weekends (>= 8pm to < 8am)
base $0
days $0.088008 per kWh = 8.8008 cents per kWh
nights $0.078008 per kWh = 7.8008 cents per kWh
delivery $3.42 + $0.035448 per kWh
*/
/* check if daytime */
if( strcmp("08:00", t) <= 0 && strcmp(t, "20:00") < 0 )
return kwh*0.088008 + kwh*0.035448;
else
return kwh*0.078008 + kwh*0.035448;
}
double produceCostPlan2(double kwh){
/*
plan 2: Secure 36 plan
base $0
all day $0.078341 per kWh = 7.8341 cents per kWh
delivery $3.42 + $0.035448 per kWh
*/
return kwh*0.078341 + kwh*0.035448;
}