#include #include #include #include #include struct scada_point{ char name[35]; char tag[20]; double current_value; double next_value; unsigned char write_to_driver; //set to 1 to write to driver, set to 0 to write to real time DB unsigned int checksum; }; /* Internal scada variables and functions exit_loop : initialized at 0,on stop of driver its value is 1 scada_db : scada vector of struct scada_point items int numSamplePoints; : number of sample point inside scada database int processed_id; : itemID of the processed value void scan_rate(void) : scan rate void lock(void) : lock access of other threads to scada_db void unlock(void) : let other threads to access scada_db void post_value() : post the processed value to real time DB or to driver */ ///////////////////////////PID implementation////////////////////////////////// struct _pid { int *pv; /*pointer to an integer that contains the process value*/ int *sp; /*pointer to an integer that contains the set point*/ float integral; float pgain; float igain; float dgain; int deadband; int last_error; }; /*------------------------------------------------------------------------ pid_init DESCRIPTION This function initializes the pointers in the _pid structure to the process variable and the setpoint. *pv and *sp are integer pointers. ------------------------------------------------------------------------*/ void pid_init(struct _pid *a, int *pv, int *sp) { a->pv = pv; a->sp = sp; } /*------------------------------------------------------------------------ pid_tune DESCRIPTION Sets the proportional gain (p_gain), integral gain (i_gain), derivitive gain (d_gain), and the dead band (dead_band) of a pid control structure _pid. ------------------------------------------------------------------------*/ void pid_tune(struct _pid *a, float p_gain, float i_gain, float d_gain, int dead_band) { a->pgain = p_gain; a->igain = i_gain; a->dgain = d_gain; a->deadband = dead_band; a->integral=0.0; a->last_error=0; } /*------------------------------------------------------------------------ get_gains DESCRIPTION Returns the gains and dead band in a _pid control structure in the locations pointed to by the p_gain, i_gain, d_gain, and dead_band pointers. ALSO SEE pid_tune ------------------------------------------------------------------------*/ void get_gains(struct _pid *a, float *p_gain, float *i_gain, float *d_gain, int *dead_band) { *p_gain = a->pgain; *i_gain = a->igain; *d_gain = a->dgain; *dead_band = a->deadband; } /*------------------------------------------------------------------------ pid_setinteg DESCRIPTION Set a new value for the integral term of the pid equation. This is useful for setting the initial output of the pid controller at start up. ------------------------------------------------------------------------*/ void pid_setinteg(struct _pid *a, float new_integ) { a->integral=new_integ; a->last_error=0; } /*------------------------------------------------------------------------ pid_bumpless DESCRIPTION Bumpless transfer algorithim. When suddenly changing setpoints, or when restarting the PID equation after an extended pause, the derivative of the equation can cause a bump in the controller output. This function will help smooth out that bump. The process value in *pv should be the updated just before this function is used. ------------------------------------------------------------------------*/ void pid_bumpless(struct _pid *a) { a->last_error = *(a->sp) - *(a->pv); } /*------------------------------------------------------------------------ pid_calc DESCRIPTION Performs PID calculations for the _pid structure *a. This function uses the positional form of the pid equation, and incorporates an integral windup prevention algorithim. Rectangular integration is used, so this function must be repeated on a consistent time basis for accurate control. RETURN VALUE The new output value for the pid loop. ------------------------------------------------------------------------*/ float pid_calc(struct _pid *a, int* pv) { int err; float pterm, dterm, result, ferror; err = *(a->sp) - *pv; if (abs(err) > a->deadband) { ferror = (float) err; /*do integer to float conversion only once*/ pterm = a->pgain * ferror; if (pterm > 100 || pterm < -100) a->integral = 0.0; else { a->integral += a->igain * ferror; if (a->integral > 100.0) a->integral=100.0; else if (a->integral < 0.0) a->integral=0.0; } dterm = (err - a->last_error) * a->dgain; result = pterm + a->integral + dterm; } else result = a->integral; a->last_error=err; //return (result > 100.0 ? 100.0 : (result < 0.0 ? 0.0 : result)); return result; } int calc_output_from_one_input(char* name_output, char* tag_output, char* name_input, int processed_id, char* processed_name, char* processed_tag, double delta, struct _pid *pid); int main(int argc, char **argv) { int itemID; //index of item in database int r; double delta = 0.1; //init pid part struct _pid PID; int process_variable, set_point; pid_init(&PID, &process_variable, &set_point); pid_tune(&PID, 4.3, 0.2, 0.1, 2); set_point = 5000; pid_setinteg(&PID,30.0); //end pid for(itemID = 0; ;) { if(exit_loop) { break; } lock(); processed_id = itemID; //////////Add your code from here///////////////////// //Example code calc_output_from_one_input("OPCUAPoint08","VALUE", "OPCUAPoint01", itemID, scada_db[itemID].name, scada_db[itemID].tag, delta, &PID); //Uncomment this to see relation between itemID and sample point name e tag name //printf("itemID = %d\n", itemID); //printf("scada_db[%d].name = %s\n", itemID, scada_db[itemID].name); //printf("scada_db[%d].tag = %s\n", itemID, scada_db[itemID].tag); //printf("scada_db[%d].current_value = %lf\n", itemID, scada_db[itemID].current_value); ///////////////////to here////////////////////////////////////////////// unlock(); fflush(stdout); if(itemID == 0) { scan_rate(); } itemID++; if(itemID == numSamplePoints) { itemID = 0; } } return 0; } int calc_output_from_one_input(char* name_output, char* tag_output, char* name_input, int processed_id, char* processed_name, char* processed_tag, double delta, struct _pid *pid) { int r; int item_id_input; double calculated; double input_value; int pv; if((r = strcmp(processed_name, name_output) == 0) && (r = strcmp(processed_tag, tag_output) == 0)) { for(item_id_input = 0; item_id_input < numSamplePoints; item_id_input++) { if(r = strcmp(scada_db[item_id_input].name, name_input) == 0) { printf("scada_db[%d].current_value = %lf\n", processed_id, scada_db[processed_id].current_value); printf("scada_db[%d].current_value = %lf\n", item_id_input, scada_db[item_id_input].current_value); printf("\n"); input_value = scada_db[item_id_input].current_value; //////////Edit here your calculation//////////////////// pv = input_value; //pid_bumpless(pid); calculated = pid_calc(pid, &pv); if((abs(scada_db[processed_id].current_value - calculated) > delta)) { scada_db[processed_id].write_to_driver = 1; scada_db[processed_id].current_value = calculated; post_value(); } return 1; } } } return 0; }