mirror of
https://github.com/thiagoralves/OpenPLC_v2.git
synced 2025-05-09 08:31:26 +08:00
183 lines
6.0 KiB
C++
183 lines
6.0 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright 2015 Thiago Alves
|
|
//
|
|
// Based on the LDmicro software by Jonathan Westhues
|
|
// 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 file is the hardware layer for the OpenPLC. If you change the platform
|
|
// where it is running, you may only need to change this file. All the I/O
|
|
// related stuff is here. Basically it provides functions to read and write
|
|
// to the OpenPLC internal buffers in order to update I/O state.
|
|
// Thiago Alves, Dec 2015
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
#include <time.h>
|
|
|
|
#include "ladder.h"
|
|
|
|
#define PORT 6668
|
|
|
|
#define ANALOG_BUF_SIZE 8
|
|
#define DIGITAL_BUF_SIZE 16
|
|
|
|
struct plcData
|
|
{
|
|
uint16_t analogIn[ANALOG_BUF_SIZE];
|
|
uint16_t analogOut[ANALOG_BUF_SIZE];
|
|
bool digitalIn[DIGITAL_BUF_SIZE];
|
|
bool digitalOut[DIGITAL_BUF_SIZE];
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helper function - Makes the running thread sleep for the ammount of time
|
|
// in milliseconds
|
|
//-----------------------------------------------------------------------------
|
|
void sleep_ms(int milliseconds)
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec = milliseconds / 1000;
|
|
ts.tv_nsec = (milliseconds % 1000) * 1000000;
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create the socket and bind it. Returns the file descriptor for the socket
|
|
// created.
|
|
//-----------------------------------------------------------------------------
|
|
int createUDPSocket(int port)
|
|
{
|
|
int socket_fd;
|
|
struct sockaddr_in server_addr;
|
|
struct hostent *server;
|
|
|
|
//Create TCP Socket
|
|
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (socket_fd<0)
|
|
{
|
|
perror("Server: error creating stream socket");
|
|
exit(1);
|
|
}
|
|
|
|
//Initialize Server Struct
|
|
bzero((char *) &server_addr, sizeof(server_addr));
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = htons(port);
|
|
server_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
//Bind socket
|
|
if (bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
|
|
{
|
|
perror("Server: error binding socket");
|
|
exit(1);
|
|
}
|
|
|
|
printf("Socket %d binded successfully on port %d!\n", socket_fd, port);
|
|
|
|
return socket_fd;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Thread to send and receive data using UDP
|
|
//-----------------------------------------------------------------------------
|
|
void *exchangeData(void *arg)
|
|
{
|
|
int socket_fd = createUDPSocket(PORT);
|
|
int net_len;
|
|
socklen_t cli_len;
|
|
struct sockaddr_in client;
|
|
struct plcData *plc_data = (struct plcData *)malloc(sizeof(struct plcData));
|
|
|
|
cli_len = sizeof(client);
|
|
|
|
while(1)
|
|
{
|
|
//printf("waiting for data...\n");
|
|
net_len = recvfrom(socket_fd, plc_data, sizeof(*plc_data), 0, (struct sockaddr *) &client, &cli_len);
|
|
//printf("data received!\n");
|
|
if (net_len < 0)
|
|
{
|
|
printf("Error receiving data on socket %d\n", socket_fd);
|
|
}
|
|
else
|
|
{
|
|
pthread_mutex_lock(&bufferLock); //lock mutex
|
|
for (int i = 0; i < ANALOG_BUF_SIZE; i++)
|
|
{
|
|
if (int_input[i] != NULL) *int_input[i] = plc_data->analogIn[i];
|
|
if (int_output[i] != NULL) plc_data->analogOut[i] = *int_output[i];
|
|
}
|
|
for (int i = 0; i < DIGITAL_BUF_SIZE; i++)
|
|
{
|
|
if (bool_input[i/8][i%8] != NULL) *bool_input[i/8][i%8] = plc_data->digitalIn[i];
|
|
if (bool_output[i/8][i%8] != NULL) plc_data->digitalOut[i] = *bool_output[i/8][i%8];
|
|
}
|
|
pthread_mutex_unlock(&bufferLock); //unlock mutex
|
|
|
|
//printf("sending data...\n");
|
|
net_len = sendto(socket_fd, plc_data, sizeof(*plc_data), 0, (struct sockaddr *) &client, cli_len);
|
|
if (net_len < 0)
|
|
{
|
|
printf("Error sending data on socket %d\n", socket_fd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function is called by the main OpenPLC routine when it is initializing.
|
|
// Hardware initialization procedures should be here.
|
|
//-----------------------------------------------------------------------------
|
|
void initializeHardware()
|
|
{
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, exchangeData, NULL);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function is called by the OpenPLC in a loop. Here the internal buffers
|
|
// must be updated to reflect the actual Input state. The mutex bufferLock
|
|
// must be used to protect access to the buffers on a threaded environment.
|
|
//-----------------------------------------------------------------------------
|
|
void updateBuffersIn()
|
|
{
|
|
// This function here is blank because the thread that connects to the
|
|
// Interface program is already filling the OpenPLC buffers with the
|
|
// data that is being received.
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This function is called by the OpenPLC in a loop. Here the internal buffers
|
|
// must be updated to reflect the actual Output state. The mutex bufferLock
|
|
// must be used to protect access to the buffers on a threaded environment.
|
|
//-----------------------------------------------------------------------------
|
|
void updateBuffersOut()
|
|
{
|
|
// This function here is blank because the thread that connects to the
|
|
// Interface program is already filling the OpenPLC buffers with the
|
|
// data that is being received.
|
|
}
|