OpenPLC_v3/webserver/core/main.cpp

288 lines
8.9 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Copyright 2018 Thiago Alves
// This file is part of the OpenPLC Software Stack.
//
// OpenPLC is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenPLC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with OpenPLC. If not, see <http://www.gnu.org/licenses/>.
//------
//
// This is the main file for the OpenPLC. It contains the initialization
// procedures for the hardware, network and the main loop
// Thiago Alves, Jun 2018
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include "iec_types.h"
#include "ladder.h"
#define OPLC_CYCLE 50000000
extern int opterr;
//extern int common_ticktime__;
IEC_BOOL __DEBUG;
IEC_LINT cycle_counter = 0;
static int tick = 0;
pthread_mutex_t bufferLock; //mutex for the internal buffers
pthread_mutex_t logLock; //mutex for the internal log
uint8_t run_openplc = 1; //Variable to control OpenPLC Runtime execution
unsigned char log_buffer[1000000]; //A very large buffer to store all logs
int log_index = 0;
int log_counter = 0;
//-----------------------------------------------------------------------------
// Helper function - Makes the running thread sleep for the ammount of time
// in milliseconds
//-----------------------------------------------------------------------------
void sleep_until(struct timespec *ts, int delay)
{
ts->tv_nsec += delay;
if(ts->tv_nsec >= 1000*1000*1000)
{
ts->tv_nsec -= 1000*1000*1000;
ts->tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ts, NULL);
}
//-----------------------------------------------------------------------------
// Helper function - Makes the running thread sleep for the ammount of time
// in milliseconds
//-----------------------------------------------------------------------------
void sleepms(int milliseconds)
{
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
nanosleep(&ts, NULL);
}
//-----------------------------------------------------------------------------
// Helper function - Logs messages and print them on the console
//-----------------------------------------------------------------------------
void log(unsigned char *logmsg)
{
pthread_mutex_lock(&logLock); //lock mutex
printf("%s", logmsg);
for (int i = 0; logmsg[i] != '\0'; i++)
{
log_buffer[log_index] = logmsg[i];
log_index++;
log_buffer[log_index] = '\0';
}
log_counter++;
if (log_counter >= 1000)
{
/*Store current log on a file*/
log_counter = 0;
log_index = 0;
}
pthread_mutex_unlock(&logLock); //unlock mutex
}
//-----------------------------------------------------------------------------
// Interactive Server Thread. Creates the server to listen to commands on
// localhost
//-----------------------------------------------------------------------------
void *interactiveServerThread(void *arg)
{
startInteractiveServer(43628);
}
//-----------------------------------------------------------------------------
// Verify if pin is present in one of the ignored vectors
//-----------------------------------------------------------------------------
bool pinNotPresent(int *ignored_vector, int vector_size, int pinNumber)
{
for (int i = 0; i < vector_size; i++)
{
if (ignored_vector[i] == pinNumber)
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Disable all outputs
//-----------------------------------------------------------------------------
void disableOutputs()
{
//Disable digital outputs
for (int i = 0; i < BUFFER_SIZE; i++)
{
for (int j = 0; j < 8; j++)
{
if (bool_output[i][j] != NULL) *bool_output[i][j] = 0;
}
}
//Disable byte outputs
for (int i = 0; i < BUFFER_SIZE; i++)
{
if (byte_output[i] != NULL) *byte_output[i] = 0;
}
//Disable analog outputs
for (int i = 0; i < BUFFER_SIZE; i++)
{
if (int_output[i] != NULL) *int_output[i] = 0;
}
}
//-----------------------------------------------------------------------------
// Special Functions
//-----------------------------------------------------------------------------
void handleSpecialFunctions()
{
//current time [%ML1024]
struct tm *current_time;
time_t rawtime;
tzset();
time(&rawtime);
current_time = localtime(&rawtime);
rawtime = rawtime - timezone;
if (current_time->tm_isdst > 0) rawtime = rawtime + 3600;
if (special_functions[0] != NULL) *special_functions[0] = rawtime;
//number of cycles [%ML1025]
cycle_counter++;
if (special_functions[1] != NULL) *special_functions[1] = cycle_counter;
//comm error counter [%ML1026]
/* Implemented in modbus_master.cpp */
//insert other special functions below
}
int main(int argc,char **argv)
{
unsigned char log_msg[1000];
sprintf(log_msg, "OpenPLC Runtime starting...\n");
log(log_msg);
//======================================================
// PLC INITIALIZATION
//======================================================
time(&start_time);
pthread_t interactive_thread;
pthread_create(&interactive_thread, NULL, interactiveServerThread, NULL);
config_init__();
glueVars();
//======================================================
// MUTEX INITIALIZATION
//======================================================
if (pthread_mutex_init(&bufferLock, NULL) != 0)
{
printf("Mutex init failed\n");
exit(1);
}
//======================================================
// HARDWARE INITIALIZATION
//======================================================
initializeHardware();
initializeMB();
initCustomLayer();
updateBuffersIn();
updateCustomIn();
updateBuffersOut();
updateCustomOut();
//======================================================
// PERSISTENT STORAGE INITIALIZATION
//======================================================
//readPersistentStorage();
//pthread_t persistentThread;
//pthread_create(&persistentThread, NULL, persistentStorage, NULL);
#ifdef __linux__
//======================================================
// REAL-TIME INITIALIZATION
//======================================================
// Set our thread to real time priority
struct sched_param sp;
sp.sched_priority = 30;
printf("Setting main thread priority to RT\n");
if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp))
{
printf("WARNING: Failed to set main thread to real-time priority\n");
}
// Lock memory to ensure no swapping is done.
printf("Locking main thread memory\n");
if(mlockall(MCL_FUTURE|MCL_CURRENT))
{
printf("WARNING: Failed to lock memory\n");
}
#endif
//gets the starting point for the clock
printf("Getting current time\n");
struct timespec timer_start;
clock_gettime(CLOCK_MONOTONIC, &timer_start);
//======================================================
// MAIN LOOP
//======================================================
while(run_openplc)
{
//make sure the buffer pointers are correct and
//attached to the user variables
glueVars();
updateBuffersIn(); //read input image
pthread_mutex_lock(&bufferLock); //lock mutex
updateCustomIn();
updateBuffersIn_MB(); //update input image table with data from slave devices
handleSpecialFunctions();
config_run__(tick++); // execute plc program logic
updateCustomOut();
updateBuffersOut_MB(); //update slave devices with data from the output image table
pthread_mutex_unlock(&bufferLock); //unlock mutex
updateBuffersOut(); //write output image
updateTime();
sleep_until(&timer_start, common_ticktime__);
}
//======================================================
// SHUTTING DOWN OPENPLC RUNTIME
//======================================================
pthread_join(interactive_thread, NULL);
printf("Disabling outputs\n");
disableOutputs();
updateCustomOut();
updateBuffersOut();
finalizeHardware();
printf("Shutting down OpenPLC Runtime...\n");
exit(0);
}