Added support for UniPi Neuron PLC

This commit is contained in:
thiagoralves 2018-10-09 11:52:58 -07:00
parent b7a534bef2
commit b551dbd3bd
4 changed files with 471 additions and 0 deletions

View File

@ -7,6 +7,7 @@ if [ $# -eq 0 ]; then
echo " win Install OpenPLC on Windows over Cygwin"
echo " linux Install OpenPLC on a Debian-based Linux distribution"
echo " rpi Install OpenPLC on a Raspberry Pi"
echo " neuron Install OpenPLC on a UniPi Neuron PLC"
echo " custom Skip all specific package installation and tries to install"
echo " OpenPLC assuming your system already has all dependencies met."
echo " This option can be useful if you're trying to install OpenPLC"
@ -335,7 +336,134 @@ WantedBy=multi-user.target" >> openplc.service
./change_hardware_layer.sh blank_linux
./compile_program.sh blank_program.st
cp ./start_openplc.sh ../../
elif [ "$1" == "neuron" ]; then
echo "Installing OpenPLC on UniPi Neuron PLC"
sudo apt-get update
sudo apt-get install build-essential pkg-config bison flex autoconf automake libtool make git python2.7 python-pip sqlite3 cmake
pip install flask
pip install flask-login
pip install pyserial
#make sure that packages are also installed for the super user
sudo -H pip install flask
sudo -H pip install flask-login
sudo -H pip install pyserial
echo ""
echo "[MATIEC COMPILER]"
cd utils/matiec_src
autoreconf -i
./configure
make
cp ./iec2c ../../webserver/
if [ $? -ne 0 ]; then
echo "Error compiling MatIEC"
echo "OpenPLC was NOT installed!"
exit 1
fi
cd ../..
echo ""
echo "[ST OPTIMIZER]"
cd utils/st_optimizer_src
g++ st_optimizer.cpp -o st_optimizer
cp ./st_optimizer ../../webserver/
if [ $? -ne 0 ]; then
echo "Error compiling ST Optimizer"
echo "OpenPLC was NOT installed!"
exit 1
fi
cd ../..
echo ""
echo "[GLUE GENERATOR]"
cd utils/glue_generator_src
g++ glue_generator.cpp -o glue_generator
cp ./glue_generator ../../webserver/core
if [ $? -ne 0 ]; then
echo "Error compiling Glue Generator"
echo "OpenPLC was NOT installed!"
exit 1
fi
cd ../..
echo ""
echo "[OPEN DNP3]"
cd utils/dnp3_src
echo "creating swapfile..."
sudo dd if=/dev/zero of=swapfile bs=1M count=1000
sudo mkswap swapfile
sudo swapon swapfile
cmake ../dnp3_src
make
sudo make install
if [ $? -ne 0 ]; then
echo "Error installing OpenDNP3"
echo "OpenPLC was NOT installed!"
exit 1
fi
sudo ldconfig
echo "removing swapfile..."
sudo swapoff swapfile
sudo rm -f ./swapfile
cd ../..
echo ""
echo "[LIBMODBUS]"
cd utils/libmodbus_src
./autogen.sh
./configure
sudo make install
if [ $? -ne 0 ]; then
echo "Error installing Libmodbus"
echo "OpenPLC was NOT installed!"
exit 1
fi
sudo ldconfig
cd ../..
echo ""
echo "[DISABLING UNIPI SERVICES]"
sudo systemctl stop neuronhost.service
sudo systemctl disable neuronhost.service
sudo systemctl stop neurontcp.service
sudo systemctl disable neurontcp.service
sudo systemctl stop evok.service
sudo systemctl disable evok.service
echo ""
echo "[OPENPLC SERVICE]"
WORKING_DIR=$(pwd)
echo -e "[Unit]\n\
Description=OpenPLC Service\n\
After=network.target\n\
\n\
[Service]\n\
Type=simple\n\
Restart=always\n\
RestartSec=1\n\
User=root\n\
Group=root\n\
WorkingDirectory=$WORKING_DIR\n\
ExecStart=$WORKING_DIR/start_openplc.sh\n\
\n\
[Install]\n\
WantedBy=multi-user.target" >> openplc.service
sudo cp -rf ./openplc.service /lib/systemd/system/
rm -rf openplc.service
echo "Enabling OpenPLC Service..."
sudo systemctl daemon-reload
sudo systemctl enable openplc
echo ""
echo "[FINALIZING]"
cd webserver/scripts
./change_hardware_layer.sh blank_linux
./compile_program.sh blank_program.st
cp ./start_openplc.sh ../../
elif [ "$1" == "custom" ]; then

View File

@ -0,0 +1,334 @@
//-----------------------------------------------------------------------------
// Copyright 2018 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, Oct 2018
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include "ladder.h"
#include "custom_layer.h"
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif
char digital_inputs[1000][200];
char digital_outputs[1000][200];
char analog_inputs[1000][200];
char analog_outputs[1000][200];
//-----------------------------------------------------------------------------
// This function is responsible for making I/O requests using SYSFS
//-----------------------------------------------------------------------------
int requestSYSFS(char *path, char *command)
{
int fd, len;
char buf[100];
if (!strncmp(command, "read", 4))
{
fd = open(path, O_RDONLY);
if (fd < 0)
{
close(fd);
return -1;
}
read(fd, buf, 100);
close(fd);
return atoi(buf);
}
else if (!strncmp(command, "write", 5))
{
fd = open(path, O_WRONLY);
if (fd < 0)
{
return -1;
}
int a = 6, b = 0;
while (command[a] != '\0')
{
buf[b] = command[a];
a++;
b++;
buf[b] = '\0';
}
write(fd, buf, b);
close(fd);
}
}
//-----------------------------------------------------------------------------
// Look for all available I/Os connected to Neuron. Scan from 1_01 to 10_10
//-----------------------------------------------------------------------------
void searchForIO()
{
char path[200];
char path_fmt[200];
unsigned char log_msg[1000];
sprintf(log_msg, "Neuron: Searching for I/O...\n");
log(log_msg);
/* look for digital inputs */
strcpy(path_fmt, "/sys/devices/platform/unipi_plc/io_group%d/di_%d_%02d/di_value");
int index = 0;
for (int group = 1; group < 10; group++)
{
for (int major = 1; major < 10; major++)
{
for (int minor = 1; minor < 10; minor++)
{
sprintf(path, path_fmt, group, major, minor);
char *command = "read";
if (requestSYSFS(path, command) >= 0)
{
/* valid I/O. Add to the list */
strcpy(digital_inputs[index], path);
index++;
digital_inputs[index][0] = '\0';
}
}
}
}
/* look for digital outputs */
strcpy(path_fmt, "/sys/devices/platform/unipi_plc/io_group%d/do_%d_%02d/do_value");
index = 0;
for (int group = 1; group < 10; group++)
{
for (int major = 1; major < 10; major++)
{
for (int minor = 1; minor < 10; minor++)
{
sprintf(path, path_fmt, group, major, minor);
char *command = "read";
if (requestSYSFS(path, command) >= 0)
{
/* valid I/O. Add to the list */
strcpy(digital_outputs[index], path);
index++;
digital_outputs[index][0] = '\0';
}
}
}
}
/* look for analog inputs */
strcpy(path_fmt, "/sys/devices/platform/unipi_plc/io_group%d/ai_%d_%d/in_voltage0_raw");
index = 0;
for (int group = 1; group < 10; group++)
{
for (int major = 1; major < 10; major++)
{
for (int minor = 1; minor < 10; minor++)
{
sprintf(path, path_fmt, group, major, minor);
char *command = "read";
if (requestSYSFS(path, command) >= 0)
{
/* valid I/O. Add to the list */
strcpy(analog_inputs[index], path);
index++;
analog_inputs[index][0] = '\0';
}
}
}
}
/* look for analog outputs */
strcpy(path_fmt, "/sys/devices/platform/unipi_plc/io_group%d/ao_%d_%d/out_voltage0_raw");
index = 0;
for (int group = 1; group < 10; group++)
{
for (int major = 1; major < 10; major++)
{
for (int minor = 1; minor < 10; minor++)
{
sprintf(path, path_fmt, group, major, minor);
char *command = "read";
if (requestSYSFS(path, command) >= 0)
{
/* valid I/O. Add to the list */
strcpy(analog_outputs[index], path);
index++;
analog_outputs[index][0] = '\0';
}
}
}
}
/* print found I/Os on console log */
sprintf(log_msg, "Neuron: Done!\n\nNeuron Digital Inputs\n");
log(log_msg);
index = 0;
while (digital_inputs[index][0] != '\0')
{
sprintf(log_msg, "%s\t=>\t%%IX%d.%d\n", digital_inputs[index], (index/8), (index%8));
log(log_msg);
index++;
}
sprintf(log_msg, "\nNeuron Digital Outputs\n");
log(log_msg);
index = 0;
while (digital_outputs[index][0] != '\0')
{
sprintf(log_msg, "%s\t=>\t%%QX%d.%d\n", digital_outputs[index], (index/8), (index%8));
log(log_msg);
index++;
}
sprintf(log_msg, "\nNeuron Analog Inputs\n");
log(log_msg);
index = 0;
while (analog_inputs[index][0] != '\0')
{
sprintf(log_msg, "%s\t=>\t%%IW%d\n", analog_inputs[index], index);
log(log_msg);
index++;
}
sprintf(log_msg, "\nNeuron Analog Outputs\n");
log(log_msg);
index = 0;
while (analog_outputs[index][0] != '\0')
{
//check if this is the last message
if (analog_outputs[index+1][0] == '\0')
sprintf(log_msg, "%s\t=>\t%%QW%d\n\n", analog_outputs[index], index);
else
sprintf(log_msg, "%s\t=>\t%%QW%d\n", analog_outputs[index], index);
log(log_msg);
index++;
}
}
//-----------------------------------------------------------------------------
// This function is called by the main OpenPLC routine when it is initializing.
// Hardware initialization procedures should be here.
//-----------------------------------------------------------------------------
void initializeHardware()
{
searchForIO();
}
//-----------------------------------------------------------------------------
// 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()
{
pthread_mutex_lock(&bufferLock); //lock mutex
/* read digital inputs */
int i = 0;
while (digital_inputs[i][0] != '\0')
{
if (pinNotPresent(ignored_bool_inputs, ARRAY_SIZE(ignored_bool_inputs), i))
if (bool_input[i/8][i%8] != NULL) *bool_input[i/8][i%8] = requestSYSFS(digital_inputs[i], "read");
i++;
}
/* read analog inputs */
i = 0;
while (analog_inputs[i][0] != '\0')
{
if (pinNotPresent(ignored_int_inputs, ARRAY_SIZE(ignored_int_inputs), i))
if (int_input[i] != NULL)
{
uint32_t value = (uint32_t)((float)requestSYSFS(analog_inputs[i], "read") * 6.5535);
if (value > 65535) value = 65535;
*int_input[i] = (uint16_t)value;
}
i++;
}
pthread_mutex_unlock(&bufferLock); //unlock mutex
}
//-----------------------------------------------------------------------------
// 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()
{
pthread_mutex_lock(&bufferLock); //lock mutex
/* write digital outputs */
int i = 0;
while (digital_outputs[i][0] != '\0')
{
if (pinNotPresent(ignored_bool_outputs, ARRAY_SIZE(ignored_bool_outputs), i))
{
if (bool_output[i/8][i%8] != NULL)
{
if (*bool_output[i/8][i%8])
requestSYSFS(digital_outputs[i], "write=1");
else
requestSYSFS(digital_outputs[i], "write=0");
}
}
i++;
}
/* write analog outputs */
i = 0;
while (analog_outputs[i][0] != '\0')
{
if (pinNotPresent(ignored_int_outputs, ARRAY_SIZE(ignored_int_outputs), i))
{
if (int_output[i] != NULL)
{
char value_fmt[100];
char value[100];
strcpy(value_fmt, "write=%f");
sprintf(value, value_fmt, ((float)*int_output[i]/6.5535));
requestSYSFS(analog_outputs[i], value);
}
}
i++;
}
pthread_mutex_unlock(&bufferLock); //unlock mutex
}

View File

@ -30,6 +30,13 @@ elif [ "$1" == "fischertechnik" ]; then
echo "Setting Platform"
echo rpi > ../scripts/openplc_platform
echo fischertechnik > ../scripts/openplc_driver
elif [ "$1" == "neuron" ]; then
echo "Activating Neuron driver"
cp ./hardware_layers/neuron.cpp ./hardware_layer.cpp
echo "Setting Platform"
echo rpi > ../scripts/openplc_platform
echo neuron > ../scripts/openplc_driver
elif [ "$1" == "pixtend" ]; then
echo "Activating PiXtend driver"

View File

@ -1285,6 +1285,8 @@ def hardware():
else: return_str += "<option value='blank_linux'>Blank with DNP3 (Linux only)</option>"
if (current_driver == "fischertechnik"): return_str += "<option selected='selected' value='fischertechnik'>Fischertechnik</option>"
else: return_str += "<option value='fischertechnik'>Fischertechnik</option>"
if (current_driver == "neuron"): return_str += "<option selected='selected' value='neuron'>Neuron</option>"
else: return_str += "<option value='neuron'>Neuron</option>"
if (current_driver == "pixtend"): return_str += "<option selected='selected' value='pixtend'>PiXtend</option>"
else: return_str += "<option value='pixtend'>PiXtend</option>"
if (current_driver == "pixtend_2s"): return_str += "<option selected='selected' value='pixtend_2s'>PiXtend 2s</option>"