Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • silecs/opensilecs
  • k.fugiel/opensilecs
  • s.kupiecki/opensilecs
3 results
Show changes
Showing
with 759 additions and 1623 deletions
#!/bin/sh
set -e
RELEASE_DIR_BASE=$1
SILECS_VERSION=$2
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT") # path where this script is located in
RELEASE_DIR=${RELEASE_DIR_BASE}/${SILECS_VERSION}/silecs-cli-client
if [ -d ${RELEASE_DIR} ]; then
echo "Error: ${RELEASE_DIR} already exists ...skipping"
exit 1
fi
SNAP7_BASE=${RELEASE_DIR_BASE}/snap7/latest/x86_64-linux
make -C ${SCRIPTPATH} clean
make -C ${SCRIPTPATH} CPU=x86_64 MAJOR=${MAJOR} MINOR=${MINOR} PATCH=${PATCH} SNAP7_BASE=${SNAP7_BASE} -j4
mkdir -p ${RELEASE_DIR}
cp -r ${SCRIPTPATH}/build/bin ${RELEASE_DIR}
cp -r ${SCRIPTPATH}/examples ${RELEASE_DIR}
# Make all files write-protected to prevent overwriting an old version by accident
chmod a-w -R ${RELEASE_DIR}
\ No newline at end of file
......@@ -24,11 +24,9 @@
#include <silecs-communication/interface/utility/XMLParser.h>
#include <silecs-communication/interface/core/SilecsService.h>
#include <silecs-communication/interface/equipment/SilecsCluster.h>
#include <silecs-communication/interface/equipment/SilecsDevice.h>
#include <silecs-communication/interface/equipment/SilecsRegister.h>
#include <boost/assign/list_of.hpp> // init vector of strings
#include <silecs-communication/interface/equipment/SilecsPLC.h>
const std::string startbold = "\e[1m";
const std::string endbold = "\e[0m";
......@@ -154,12 +152,18 @@ std::string getPLCName(Silecs::XMLParser &paramParser)
return mappingNode.getAttribute("plc-name");
}
Silecs::Cluster* getSilecsClusterbyDevice(std::string deviceName, Silecs::XMLParser &paramParser, Silecs::Service *silecsService)
Silecs::PLC& getSilecsPLC(const std::string& plcName, Silecs::Service *silecsService)
{
return silecsService->getPLCHandler().getPLC(plcName);
}
Silecs::SilecsDesign& getSilecsDesignbyDevice(std::string deviceName, Silecs::XMLParser &paramParser, Silecs::Service *silecsService)
{
Silecs::ElementXML classNode = paramParser.getFirstElementFromXPath("/SILECS-Param/SILECS-Mapping/SILECS-Class[Instance/@label='" + deviceName + "']");
std::string className = classNode.getAttribute("name");
std::string classVersion = classNode.getAttribute("version");
return silecsService->getCluster(className, classVersion);
auto& plc = getSilecsPLC(getPLCName(paramParser), silecsService);
return plc.getDeploy().getDesign(className);
}
std::string getSilecsBlockNamebyRegisterName(std::string registerName, Silecs::XMLParser &paramParser)
......@@ -179,9 +183,8 @@ bool isRegisterInBlock(std::string registerName, std::string blockName, Silecs::
std::vector<std::string> getDeviceNames(Silecs::XMLParser &paramParser)
{
std::vector < std::string > deviceNames;
boost::ptr_vector<Silecs::ElementXML> deviceNodes = paramParser.getElementsFromXPath_throwIfEmpty("/SILECS-Param/SILECS-Mapping/SILECS-Class/Instance");
boost::ptr_vector<Silecs::ElementXML>::iterator deviceNode;
for (deviceNode = deviceNodes.begin(); deviceNode != deviceNodes.end(); deviceNode++)
auto deviceNodes = paramParser.getElementsFromXPath("/SILECS-Param/SILECS-Mapping/SILECS-Class/Instance");
for (auto deviceNode = deviceNodes.begin(); deviceNode != deviceNodes.end(); deviceNode++)
{
deviceNames.push_back(deviceNode->getAttribute("label"));
}
......@@ -193,9 +196,8 @@ std::vector<std::string> getBlockNamesFromDeviceName(std::string deviceName, Sil
std::vector < std::string > blockNames;
Silecs::ElementXML classNode = paramParser.getFirstElementFromXPath("/SILECS-Param/SILECS-Mapping/SILECS-Class[Instance/@label='" + deviceName + "']");
std::string className = classNode.getAttribute("name");
boost::ptr_vector<Silecs::ElementXML> blocks = paramParser.getElementsFromXPath_throwIfEmpty("/SILECS-Param/SILECS-Mapping/SILECS-Class[@name='" + className + "']/*[ name()='Acquisition-Block' or name()='Setting-Block' or name()='Command-Block']");
boost::ptr_vector<Silecs::ElementXML>::iterator block;
for (block = blocks.begin(); block != blocks.end(); block++)
auto blocks = paramParser.getElementsFromXPath("/SILECS-Param/SILECS-Mapping/SILECS-Class[@name='" + className + "']/*[ name()='Acquisition-Block' or name()='Setting-Block' or name()='Command-Block']");
for (auto block = blocks.begin(); block != blocks.end(); block++)
{
//std::cout<< block->getAttribute("name") << std::endl;
blockNames.push_back(block->getAttribute("name"));
......@@ -208,9 +210,8 @@ std::vector<std::string> getRegisterNamesFromDeviceBlockName(std::string deviceN
std::vector < std::string > registerNames;
Silecs::ElementXML classNode = paramParser.getFirstElementFromXPath("/SILECS-Param/SILECS-Mapping/SILECS-Class[Instance/@label='" + deviceName + "']");
std::string className = classNode.getAttribute("name");
boost::ptr_vector<Silecs::ElementXML> registerNodes = paramParser.getElementsFromXPath_throwIfEmpty("/SILECS-Param/SILECS-Mapping/SILECS-Class[@name='" + className + "']/*[@name='" + blockName + "']/*[ name()='Acquisition-Register' or name()='Setting-Register' or name()='Volatile-Register']");
boost::ptr_vector<Silecs::ElementXML>::iterator registerNode;
for (registerNode = registerNodes.begin(); registerNode != registerNodes.end(); registerNode++)
auto registerNodes = paramParser.getElementsFromXPath("/SILECS-Param/SILECS-Mapping/SILECS-Class[@name='" + className + "']/*[@name='" + blockName + "']/*[ name()='Acquisition-Register' or name()='Setting-Register' or name()='Volatile-Register']");
for (auto registerNode = registerNodes.begin(); registerNode != registerNodes.end(); registerNode++)
{
//std::cout<< block->getAttribute("name") << std::endl;
registerNames.push_back(registerNode->getAttribute("name"));
......@@ -236,10 +237,10 @@ void printTableHead()
std::cout << "------------------------------------------------------------------------------------------------------------------------------------------------" << std::endl;
}
void printRunState(Silecs::PLC *plc)
void printRunState(Silecs::PLC& plc)
{
std::cout << "------------------------------------------------------------------------------------------------------------------------------------------------" << std::endl;
if (plc->isRunning())
if (plc.isRunning())
std::cout << "plc run-state is: RUNNING" << std::endl;
else
std::cout << "plc run-state is: STOPPED" << std::endl;
......@@ -261,13 +262,12 @@ void printRegister(Silecs::Device *device, Silecs::Register *reg)
void printBlock(Silecs::Device *device, std::string blockName, Silecs::XMLParser &paramParser)
{
device->recv(blockName);
std::vector<Silecs::Register*> regCol = device->getRegisterCollection(blockName);
std::vector<Silecs::Register*>::iterator reg;
for (reg = regCol.begin(); reg != regCol.end(); reg++)
auto& regCol = device->getRegisterCollection(blockName);
for (auto reg = regCol.begin(); reg != regCol.end(); reg++)
{
if (isRegisterInBlock( (*reg)->getName(), blockName, paramParser))
{
printRegister(device, *reg);
printRegister(device, (*reg).get());
}
}
}
......@@ -279,11 +279,10 @@ void printDevice(Silecs::Device *device, Silecs::XMLParser &paramParser)
for (blockName = blockNames.begin(); blockName != blockNames.end(); blockName++)
{
device->recv(*blockName);
std::vector<Silecs::Register*> regCol = device->getRegisterCollection(*blockName);
std::vector<Silecs::Register*>::iterator reg;
for (reg = regCol.begin(); reg != regCol.end(); reg++)
auto& regCol = device->getRegisterCollection(*blockName);
for (auto reg = regCol.begin(); reg != regCol.end(); reg++)
{
printRegister(device, *reg);
printRegister(device, (*reg).get());
}
}
}
......@@ -296,11 +295,9 @@ void setRegister(Silecs::Register *reg, std::string value)
int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
{
std::vector < std::string > mainMenu = boost::assign::list_of("connect to plc-device")("select block")("select register")("print whole device")("print block")("print register")("query plc run state")("cold-restart plc");
Silecs::Cluster *silecsCluster = NULL;
Silecs::PLC *plc = NULL;
Silecs::Device *device = NULL;
Silecs::Register *reg = NULL;
std::vector <std::string> mainMenu = {"connect to plc-device", "select block",
"select register", "print whole device", "print block", "print register",
"query plc run state", "cold-restart plc"};
while (true)
{
......@@ -321,10 +318,8 @@ int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
arg_deviceName = devices[index];
arg_blockName = "";
arg_registerName = "";
silecsCluster = getSilecsClusterbyDevice(arg_deviceName, paramParser, service);
plc = silecsCluster->getPLC(getPLCName(paramParser), arg_parameterFile);
plc->connect(Silecs::MASTER_SYNCHRO, true, arg_checkChecksum);
device = plc->getDevice(arg_deviceName);
auto& plc = getSilecsPLC(getPLCName(paramParser), service);
plc.connect(true, arg_checkChecksum);
break;
}
case 1:
......@@ -356,10 +351,10 @@ int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
if (index == -1)
break;
arg_registerName = registers[index];
reg = device->getRegister(arg_registerName);
break;
}
case 3:
{
if (arg_deviceName.empty())
{
std::cout << "Please connect to a device first! - Press any key to continue." << std::endl;
......@@ -367,9 +362,13 @@ int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
break;
}
printTableHead();
auto& design = getSilecsDesignbyDevice(arg_deviceName, paramParser, service);
auto device = design.getDevice(arg_deviceName);
printDevice(device, paramParser);
break;
}
case 4:
{
if (arg_deviceName.empty() || arg_blockName.empty())
{
std::cout << "Please first connect to a device and select a block! - Press any key to continue." << std::endl;
......@@ -377,9 +376,13 @@ int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
break;
}
printTableHead();
auto& design = getSilecsDesignbyDevice(arg_deviceName, paramParser, service);
auto device = design.getDevice(arg_deviceName);
printBlock(device, arg_blockName, paramParser);
break;
}
case 5:
{
if (arg_deviceName.empty() || arg_blockName.empty() || arg_registerName.empty())
{
std::cout << "Please first connect to device and pick a block and a register! - Press any key to continue." << std::endl;
......@@ -387,26 +390,36 @@ int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
break;
}
printTableHead();
printRegister(device, reg);
auto& design = getSilecsDesignbyDevice(arg_deviceName, paramParser, service);
auto device = design.getDevice(arg_deviceName);
auto& reg = device->getRegister(arg_registerName);
printRegister(device, reg.get());
break;
}
case 6:
{
if (arg_deviceName.empty())
{
std::cout << "Please connect to a device first! - Press any key to continue." << std::endl;
getch();
break;
}
auto& plc = getSilecsPLC(getPLCName(paramParser), service);
printRunState(plc);
break;
}
case 7:
{
if (arg_deviceName.empty())
{
std::cout << "Please connect to a device first! - Press any key to continue." << std::endl;
getch();
break;
}
plc->sendColdRestart();
auto& plc = getSilecsPLC(getPLCName(paramParser), service);
plc.sendColdRestart();
break;
}
default:
std::cout << "Invalid option:" << item << std::endl;
return EXIT_FAILURE;
......@@ -418,10 +431,9 @@ int connectInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser)
int connectNonInteractive(Silecs::Service *service, Silecs::XMLParser &paramParser, bool periodicOptionSet)
{
Silecs::Cluster *silecsCluster = getSilecsClusterbyDevice(arg_deviceName, paramParser, service);
Silecs::PLC *plc = silecsCluster->getPLC(getPLCName(paramParser), arg_parameterFile);
plc->connect(Silecs::MASTER_SYNCHRO, true, arg_checkChecksum);
if (!plc->isConnected())
auto& plc = getSilecsPLC(getPLCName(paramParser), service);
plc.connect(true, arg_checkChecksum);
if (!plc.isConnected())
{
std::cout << "Error: Failed to connect to PLC." << std::endl;
return EXIT_FAILURE;
......@@ -431,12 +443,13 @@ int connectNonInteractive(Silecs::Service *service, Silecs::XMLParser &paramPars
{
printRunState(plc);
}
Silecs::Device *device = plc->getDevice(arg_deviceName);
auto& design = getSilecsDesignbyDevice(arg_deviceName, paramParser, service);
auto device = design.getDevice(arg_deviceName);
Silecs::Register *reg = NULL;
if (!arg_registerName.empty())
{
reg = device->getRegister(arg_registerName);
reg = device->getRegister(arg_registerName).get();
arg_blockName = getSilecsBlockNamebyRegisterName(arg_registerName, paramParser);
}
......
# silecs-cli
CLI client for silecs project creation, xml validation, code generation and opening of the diagnostic GUI
#!/bin/sh
set -e
RELEASE_DIR_BASE=$1
SILECS_VERSION=$2
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT") # path where this script is located in
RELEASE_DIR=${RELEASE_DIR_BASE}/${SILECS_VERSION}/silecs-cli
if [ -d ${RELEASE_DIR} ]; then
echo "Error: ${RELEASE_DIR} already exists ...skipping"
exit 1
fi
mkdir -p ${RELEASE_DIR}
DEST_FILE=${RELEASE_DIR}/silecs
cp -r ${SCRIPTPATH}/silecs.sh ${DEST_FILE}
# Make all files write-protected to prevent overwriting an old version by accident
chmod a-w -R ${RELEASE_DIR}
\ No newline at end of file
#!/bin/sh
# File/Project has to be the last argument
FILE=${@:$#}
PROJECT_NAME=$(echo "${FILE##*/}" | sed 's/\.[^.]*$//')
SCRIPT=$(readlink -f "$0")
SILECS_VERSION=2.3.0
# Prefill $SILECS_BASE with a local path, if you want to test a local installation
if [ -z $SILECS_BASE ]; then
SILECS_BASE=/common/usr/cscofe/silecs/${SILECS_VERSION}
fi
echo "using sileces base path: $SILECS_BASE"
FESA_VERSION=7.4.0
FESA_BASE=/opt/fesa/fesa-fwk/$FESA_VERSION
N_ARGUMENTS=$#
function help {
echo -e ""
echo -e "Usage: silecs [OPTION] [FILE]"
echo -e ""
echo -e "Script in order to model PLC devices in FESA by using the Silecs framework."
echo -e ""
echo -e "\t -c \t create new .silecsdesign/.silecsdeploy file for the specified FESA .design/.deploy file"
echo -e "\t -v \t validate silecs xml file"
echo -e "\t -g \t generate source code for silecs xml file"
echo -e "\t -d \t start diagnostic tool for silecs xml file"
echo -e "\t -r \t release parameter file to FEC (TODO)"
echo -e "\t -m \t migrate silecs design/deploy file to new silecs version (TODO)"
echo -e "\t -h \t show this help"
echo -e ""
echo -e "Examples:"
echo -e ""
echo -e "\t To create a new file 'MyClass.silecsdesign' for the FESA class."
echo -e "\t\t silces -c ~/workspace/MyClass/src/MyClass.design"
echo -e ""
echo -e "\t To validate 'MyClass.silecsdesign'."
echo -e "\t\t silces -v ~/workspace/MyClass/src/MyClass.silecsdesign"
echo -e ""
echo -e "\t To add missing properties and value-items specified in 'MyClass.silecsdesign' into the FESA document 'MyClass.design'."
echo -e "\t This will as well ate C++ code."
echo -e "\t\t silces -g ~/workspace/MyClass/src/MyClass.silecsdesign"
echo -e ""
echo -e "Check the Silecs Wiki for more info: https://www-acc.gsi.de/wiki/Frontend/SILECS"
echo -e ""
exit 1;
}
if [ $N_ARGUMENTS -lt 1 ]
then
echo "Error: Wrong number of arguments supplied."
help
fi
function assert_n_arguments {
if [ $N_ARGUMENTS -lt $1 ]
then
echo "Error: Wrong number of arguments supplied."
help
fi
}
function silecs_validate {
assert_n_arguments 2
EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
# Resolve possible relative path
FILE=$(realpath "$FILE" )
case $EXTENSION in
silecsdesign)
SCHEMA=$SILECS_BASE/silecs-model/xml/DesignSchema.xsd
;;
silecsdeploy)
SCHEMA=$SILECS_BASE/silecs-model/xml/DeploySchema.xsd
;;
*)
echo "Error: Passed file for validation needs to have the extension '.silecsdesign' or '.silecsdeploy'"
exit 1
;;
esac
echo "validating $FILE"
echo ""
xmllint --schema $SCHEMA $FILE --noout
RESULT=$?
echo ""
if [ $RESULT -eq 0 ]
then
echo "File is valid"
else
echo "File is not valid. Check errors above."
fi
return $RESULT
# TODO: Implement jave-rules - See here for details: https://gitlab.com/al.schwinn/silecs-cli/-/issues/3
}
function execute_python {
PYTHON_FILE=$1
PYTHON_METHOD=$2
SILECS_CODEGEN_BASE=$SILECS_BASE/silecs-codegen/xml/
SILECS_CODEGEN_MIGRATION=$SILECS_BASE/silecs-codegen/xml/migration
# TODO: Check for concrete fesa version - See here for details: https://gitlab.com/al.schwinn/silecs-cli/-/issues/4
SILECS_CODEGEN_FESA=$SILECS_BASE/silecs-codegen/xml/fesa/fesa_7_3_0/
ARGUMENTS=""
for ((i=3; i<=$#; i++))
do
ARGUMENTS+="'"
ARGUMENTS+=${!i}
ARGUMENTS+="'"
if [ $i -ne $# ]
then
ARGUMENTS+=","
fi
done
#echo $ARGUMENTS
python3 -c "import sys;\
sys.path.append('$SILECS_CODEGEN_BASE');\
sys.path.append('$SILECS_CODEGEN_MIGRATION');\
sys.path.append('$SILECS_CODEGEN_FESA');\
import $PYTHON_FILE;\
$PYTHON_FILE.$PYTHON_METHOD($ARGUMENTS);"
return $?
}
function silecs_generate {
assert_n_arguments 2
silecs_validate
if [ $? -ne 0 ]; then
echo ""
echo "Error: $FILE is not valid. Please fix all xml errors before generating code !"
exit 1
fi
# Resolve possible relative path
FILE=$(realpath "$FILE" )
EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
case $EXTENSION in
silecsdesign)
silecs_generate_silecsdesign
;;
silecsdeploy)
silecs_generate_silecsdeploy
;;
*)
echo "Error: Passed file for code generation needs to have the exctension '.silecsdesign' or '.silecsdeploy'"
exit 1
;;
esac
}
function silecs_generate_silecsdesign {
SILECS_DESIGN_FILE=$FILE
PROJECT_PATH=$(dirname "$(dirname "$FILE")")
WORKSPACE_PATH=$(dirname "$PROJECT_PATH")
SILECS_LIBRARY_BASE=$SILECS_BASE/silecs-communication-cpp
FESA_DESIGN_FILE=$PROJECT_PATH/src/$PROJECT_NAME.design
FESA_XSD=$FESA_BASE/fesa-model-gsi/xml/design/design-gsi.xsd
echo "generating code for $PROJECT_NAME ..."
echo ""
if [ -f $FESA_DESIGN_FILE ];
then
echo "Please note that old, obsolete xml-elements are not deleted automatically! Sometimes this can lead to an invalid FESA Design document!"
echo "If this is the case, please remove the obsolete elements by hand."
echo "Creating a backup of the existing FESA file at: $FESA_DESIGN_FILE.backup"
echo ""
cp $FESA_DESIGN_FILE $FESA_DESIGN_FILE.backup
fi
execute_python generateFesaDesign fillDesignFile $FESA_VERSION $PROJECT_NAME $WORKSPACE_PATH $FESA_XSD
execute_python generateSourceCode genCppFiles $PROJECT_NAME $WORKSPACE_PATH $SILECS_DESIGN_FILE
echo ""
echo "Please not that you need to add the dependency to the silecs library to your Makefile."
echo "Here an example: https://www-acc.gsi.de/wiki/Frontend/Silecs_1_0_0_CodeSnippets#Dependency_in_Makefile.specific !"
echo ""
echo "Code generation finished"
}
function silecs_generate_silecsdeploy {
SILECS_DEPLOY_FILE=$FILE
PROJECT_PATH=$(dirname "$(dirname "$FILE")")
WORKSPACE_PATH=$(dirname "$PROJECT_PATH")
SILECS_LIBRARY_BASE=$SILECS_BASE/silecs-communication-cpp
FESA_DEPLOY_FILE=$PROJECT_PATH/src/$PROJECT_NAME.deploy
FESA_XSD=$FESA_BASE/fesa-model-gsi/xml/deployment/deployment-gsi.xsd
FESA_GSI_TEMPLATE=$FESA_BASE/fesa-model-gsi/xml/design/templates/GSIClassTemplate.xml
SILECS_DEPLOY_VERSION="unused_parameter"
echo "generating code for $PROJECT_NAME ..."
echo ""
if [ -f $FESA_DESIGN_FILE ];
then
echo "Please note that old, obsolete xml-elements are not deleted automatically! Sometimes this can lead to an invalid FESA document!"
echo "If this is the case, please remove the obsolete elements by hand."
echo "Creating a backup of the existing FESA file at: $FESA_DEPLOY_FILE.backup"
echo ""
cp $FESA_DEPLOY_FILE $FESA_DEPLOY_FILE.backup
fi
execute_python genparam genParam $WORKSPACE_PATH $PROJECT_NAME $SILECS_DEPLOY_VERSION $SILECS_VERSION
execute_python genplcsrc genPlcSrc $WORKSPACE_PATH $PROJECT_NAME $SILECS_VERSION
execute_python genduwrapper genDuWrapper $WORKSPACE_PATH $PROJECT_NAME $SILECS_DEPLOY_VERSION
execute_python fillFESADeployUnit fillDeployUnit $WORKSPACE_PATH $PROJECT_NAME $FESA_XSD $FESA_VERSION
echo ""
echo "Please not that you need to add the dependency to the silecs library to your Makefile."
echo "Here an example: https://www-acc.gsi.de/wiki/Frontend/Silecs_1_0_0_CodeSnippets#Dependency_in_Makefile.specific !"
echo ""
echo "Code generation finished"
}
function silecs_create_design {
NEWFILEPATH=$1
DATE=$(date +%x)
SCHEMA_PATH=$SILECS_BASE/silecs-model/xml/DesignSchema.xsd
DESIGN_TEMPLATE='<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Design silecs-version="'$SILECS_VERSION'" created="'$DATE'" updated="'$DATE'"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="'$SCHEMA_PATH'">
<Information>
<Owner user-login="'$USER'"/>
<Editor user-login="'$USER'"/>
</Information>
<SILECS-Class name="'$PROJECT_NAME'" version="0.1.0" domain="OPERATIONAL" >
<Acquisition-Block name="MyBlock" generateFesaProperty="true">
<Acquisition-Register name="myRegister" generateFesaValueItem="true">
<scalar format="int32"/>
</Acquisition-Register>
</Acquisition-Block>
</SILECS-Class>
</SILECS-Design>'
if [ -f $NEWFILEPATH ]; then
echo "Error: There is already a .silecsdesign file available: $NEWFILEPATH"
exit 1
fi
echo $DESIGN_TEMPLATE | xmllint --format - > $NEWFILEPATH
echo ""
echo "created new silecsdesign: $NEW_FILE"
}
function silecs_create_deploy {
NEWFILEPATH=$1
DATE=$(date +%x)
SCHEMA_PATH=$SILECS_BASE/silecs-model/xml/DeploySchema.xsd
DEPLOY_TEMPLATE='<?xml version="1.0" encoding="UTF-8"?>
<SILECS-Deploy silecs-version="'$SILECS_VERSION'" created="'$DATE'" updated="'$DATE'"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="'$SCHEMA_PATH'">
<Information>
<Owner user-login="'$USER'"/>
<Editor user-login="'$USER'"/>
</Information>
<Deploy-Unit name="'$PROJECT_NAME'" version="0.1.0"/>
<SilecsDesign silecs-design-name="" silecs-design-version=""/>
<Controller host-name="">
<Siemens-PLC system="TIA-PORTAL" model="SIMATIC_S7-300" protocol="DEVICE_MODE" base-DB-number="1">
<Device silecs-device-label="dev0" silecs-design-ref="" fesa-device-name="" fesa-fec-name=""/>
</Siemens-PLC>
</Controller>
</SILECS-Deploy>'
if [ -f $NEWFILEPATH ]; then
echo "Error: There is already a .silecsdeploy file available: $NEWFILEPATH"
exit 1
fi
echo $DEPLOY_TEMPLATE | xmllint --format - > $NEWFILEPATH
echo ""
echo "created new silecsdeploy: $NEW_FILE"
}
function silecs_create {
assert_n_arguments 2
# Resolve possible relative path
FILE=$(realpath "$FILE" )
EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
FILE_PATH=$(dirname "$FILE")
case $EXTENSION in
design)
NEW_FILE=$FILE_PATH/$PROJECT_NAME.silecsdesign
silecs_create_design $NEW_FILE
;;
deploy)
NEW_FILE=$FILE_PATH/$PROJECT_NAME.silecsdeploy
silecs_create_deploy $NEW_FILE
;;
*)
echo "Error: Passed FESA file needs to have the extension '.design' or '.deploy'"
exit 1
;;
esac
}
function silecs_open_diag {
assert_n_arguments 2
# Resolve possible relative path
FILE=$(realpath "$FILE" )
EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
if [ $EXTENSION -ne "silecsdeploy" ]; then
echo "Error: diag tool only can be opened for a -silecsdeploy file"
fi
SILECS_DIAG_TOOL=$SILECS_BASE/silecs-diagnostic-cpp/bin/x86_64/silecs-diagnostic
$SILECS_DIAG_TOOL -d $FILE
}
function silecs_release_param {
# TODO - See here for details: https://gitlab.com/al.schwinn/silecs-cli/-/issues/5
echo "Not implemented yet .. please copy the param file by hand for now"
}
function silecs_migrate {
# TODO - See here for details: https://gitlab.com/al.schwinn/silecs-cli/-/issues/7
echo "Not implemented yet .. please update/migrate by hand for now"
}
while getopts c:v:g:d:r:m:h flag
do
case "${flag}" in
c ) silecs_create;;
v ) silecs_validate;;
g ) silecs_generate;;
d ) silecs_open_diag;;
r ) silecs_release_param;;
m ) silecs_migrate;;
h ) help;;
esac
done
cmake_minimum_required(VERSION 3.20.2)
project(silecs-codegen)
find_package(Python COMPONENTS Interpreter)
add_test (NAME codegen-tests
COMMAND ${Python_EXECUTABLE} -m unittest
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/xml/
)
install (
DIRECTORY ${PROJECT_SOURCE_DIR}/src/xml
DESTINATION ${CMAKE_PROJECT_VERSION}/${PROJECT_NAME}
FILES_MATCHING PATTERN "*.py*")
This diff is collapsed.
# silecs-codegen
This component of the SILECS PLC-framework generates FESA and stand-alone C++ code from an existing silecs-project in order to ease the usage of the package silecs-communication-cpp
This component of the opensilecs PLC-framework generates FESA and stand-alone C++ code from an existing silecs-project in order to ease the usage of the package silecs-communication-cpp
# Disclaimer
## Tests
The GSI Version of Silecs is a fork of the CERN Silecs version. It was forked 2018 and has evolved into a slightly different framework. The fork was required because CERN did not merge GSI merge requests, and generally does not respect the GPL v3 licence (source code and issue tracker only visible in the CERN walled garden, CERN account and special privileges required, software only builds and runs within CERN infrastructure ). That makes a collaboration/contributions impossible.
In [test](src/xml/test/) directory there are files with {test_name} which contain unit tests written using `unittest` framework. Tests conserning the migration tools are in their own corresponding folders inside [migration](src/xml/migration/).
## Getting Started
Please check the SILECS-Wiki for more information:
[GSI SILECS Wiki Page][GSI_Wiki]
## License
Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. See the [LICENSE file][license] for details.
[license]: LICENSE
[GSI_Wiki]: https://www-acc.gsi.de/wiki/Frontend/SILECS
All tests can be run together using following command from inside [src/xml](src/xml/):
```
python3 -m unittest
```
#!/bin/sh
set -e
RELEASE_DIR_BASE=$1
SILECS_VERSION=$2
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT") # path where this script is located in
RELEASE_DIR=${RELEASE_DIR_BASE}/${SILECS_VERSION}/silecs-codegen
if [ -d ${RELEASE_DIR} ]; then
echo "Error: ${RELEASE_DIR} already exists ...skipping"
exit 1
fi
exit 1
cd ${SCRIPTPATH}/src/xml
python3 runTests.py
mkdir -p ${RELEASE_DIR}
cp -r ${SCRIPTPATH}/src/xml ${RELEASE_DIR}
# Make all files write-protected to prevent overwriting an old version by accident
chmod a-w -R ${RELEASE_DIR}
\ No newline at end of file
......@@ -9,6 +9,7 @@
# be specialized on call to append the string to be stored in the file.
#
import string
import time
......@@ -54,17 +55,17 @@ def toWordArray(strg):
#=========================================================================
# C Source template (.c file)
#=========================================================================
header = """
header = string.Template("""
/* +-------------------------------------------------------------------
* | Copyright CERN 2015
* | SILECS - BE/CO-SRC
* | April 2015
* +-------------------------------------------------------------------
*
* Release : %s
* Release : ${silecsRelease}
*
* The following code has been automatically generated by SILECS.
* Code regeneration will overwrite it.
*
* N.B: This file relies on the existence of explicit C data type such
* as int8_t, uint8_t, int16_t, etc....
......@@ -73,7 +74,7 @@ header = """
* web page before including this header file.
*/
#define MODBUS_START_ADDRESS %s
#define MODBUS_START_ADDRESS ${baseAddress}
/*---------------------------------------------------------------------
* DT
......@@ -94,7 +95,7 @@ typedef struct
} dt;
#define _frombcd(a) (int)(((a>>4)*10)+(a&0x0F))
#define _tobcd(a) (((unsigned char)((a)/10)<<4)+((a)%%10))
#define _tobcd(a) (((unsigned char)((a)/10)<<4)+((a)%10))
void SILECS_set_dt(int8_t sc_100 ,int8_t sc, int8_t mn,int8_t hh,int8_t dd,int8_t mm,int32_t yy, dt *date)
{
......@@ -105,80 +106,72 @@ void SILECS_set_dt(int8_t sc_100 ,int8_t sc, int8_t mn,int8_t hh,int8_t dd,int8_
date->dd = _tobcd(dd);
date->mm = _tobcd(mm);
date->yy2 = _tobcd((int8_t)(yy/100));
date->yy1 = _tobcd((int8_t)(yy%%100));
date->yy1 = _tobcd((int8_t)(yy%100));
}
"""
""")
#=========================================================================
# DATA TYPE DEFINITION
#=========================================================================
firstBlockUDT = """
firstBlockUDT = string.Template("""
/*---------------------------------------------------------------------
* %s / v%s
* ${className} / v${classVersion}
* BLOCK Type definition
*---------------------------------------------------------------------
*/
"""
""")
blockUDT = """
blockUDT = string.Template("""
typedef struct
{
%s
} _%s_%s;
"""
$regList
} _${className}_${blockName};
""")
regScalar = """ %s %s;
"""
regScalar = string.Template(""" ${regFormat} ${regName};
""")
regArray = """ %s %s[%s];
"""
regArray = string.Template(""" ${regFormat} ${regName}[${strLen}];
""")
regArray2d = """ %s %s[%s][%s];
"""
regArray2d = string.Template(""" ${regFormat} ${regName}[${regDim}][${strLen}];
""")
stringArray = """ %s %s[%s][%s][%s];
"""
stringArray = string.Template(""" ${regFormat} ${regName}[${regDim}][${regDim2}][${strLen}];
""")
#=========================================================================
# INSTANTIATION
#=========================================================================
allocationComment = """
allocationComment = string.Template("""
/*---------------------------------------------------------------------
* MEMORY ALLOCATION
* PROTOCOL: %s
* PROTOCOL: ${plcProtocol}
*---------------------------------------------------------------------
*/
"""
""")
#======= DEVICE MODE =================
deviceModeBlockInstantiation = string.Template(""" _${className}_${blockName} ${blockName};
""")
deviceModeBlockInstantiation = """ _%s_%s %s;
"""
#deviceModeClass_deviceNumber = """
# struct {
# _%s_%s %s;
# } %s_device[%s];
#"""
deviceModeClass_deviceNumber = """
deviceModeClass_deviceNumber = string.Template("""
struct {
%s
} %s_device[NB_%s_DEVICE];
"""
deviceModeClass_deviceList = """
${blockList}
} ${className}_device[NB_${className}_DEVICE];
""")
deviceModeClass_deviceList = string.Template("""
struct {
%s
} %s;
"""
${blockList}
} ${deviceList};
""")
deviceMode_dataInstantiation = """
deviceMode_dataInstantiation = string.Template("""
typedef struct {
%s
${classList}
} _SILECS_DATA_SEGMENT;
#define SILECS_DATA_SEGMENT_MODBUS_SIZE (sizeof(_SILECS_DATA_SEGMENT)/2)
......@@ -188,21 +181,20 @@ union silecsData {
uint16_t array[SILECS_DATA_SEGMENT_MODBUS_SIZE];
} silecsData;
"""
""")
#======= BLOCK MODE =================
blockModeDeviceInstantiation_deviceList = string.Template(""" _${className}_${blockName} ${deviceName};
""")
blockModeDeviceInstantiation_deviceList = """ _%s_%s %s;
"""
blockModeBlockInstantiation = """
blockModeBlockInstantiation = string.Template("""
struct {
%s } %s_%s;
"""
${deviceList} } ${className}_${blockName};
""")
blockMode_dataInstantiation = """
blockMode_dataInstantiation = string.Template("""
typedef struct {
%s
${classList}
} _SILECS_DATA_SEGMENT;
#define SILECS_DATA_SEGMENT_MODBUS_SIZE (sizeof(_SILECS_DATA_SEGMENT)/2)
......@@ -212,7 +204,7 @@ union modbus_data {
uint16_t array[SILECS_DATA_SEGMENT_MODBUS_SIZE];
} silecsData;
"""
""")
#=========================================================================
# init
......@@ -223,44 +215,43 @@ globalAllocation = """
uint16_t modbus_data = & data;
"""
initFunctionDeviceMode = """
initFunctionDeviceMode = string.Template("""
/* Initialization function */
int SILECS_init()
{
/* Silecs version initialization */
strcpy((unsigned char *)silecsData.data.SilecsHeader_device[0].hdrBlk._version, "%s");
strcpy((unsigned char *)silecsData.data.SilecsHeader_device[0].hdrBlk._version, "${version}");
/* Silecs checksum initialization */
silecsData.data.SilecsHeader_device[0].hdrBlk._checksum = %s;
silecsData.data.SilecsHeader_device[0].hdrBlk._checksum = ${checksum};
/* Silecs user initialization */
strcpy((unsigned char *)silecsData.data.SilecsHeader_device[0].hdrBlk._user, "%s");
strcpy((unsigned char *)silecsData.data.SilecsHeader_device[0].hdrBlk._user, "${user}");
/* Silecs date initialization */
SILECS_set_dt(%s,%s,%s,%s,%s,%s,%s,&silecsData.data.SilecsHeader_device[0].hdrBlk._date);
SILECS_set_dt(${dt6},${dt5},${dt4},${dt3},${dt2},${dt1},${dt0},&silecsData.data.SilecsHeader_device[0].hdrBlk._date);
}
"""
""")
#initFunctionBlockMode = """
initFunctionBlockMode = """
initFunctionBlockMode = string.Template("""
/* Initialization function */
int SILECS_init()
{
/* Silecs version initialization */
strcpy((unsigned char *)silecsData.data.SilecsHeader_hdrBlk.device[0]._version, "%s");
strcpy((unsigned char *)silecsData.data.SilecsHeader_hdrBlk.device[0]._version, "${version}");
/* Silecs checksum initialization */
silecsData.data.SilecsHeader_hdrBlk.device[0]._checksum = %s;
silecsData.data.SilecsHeader_hdrBlk.device[0]._checksum = ${checksum};
/* Silecs user initialization */
strcpy((unsigned char *)silecsData.data.SilecsHeader_hdrBlk.device[0]._user, "%s");
strcpy((unsigned char *)silecsData.data.SilecsHeader_hdrBlk.device[0]._user, "${user}");
/* Silecs date initialization */
SILECS_set_dt(%s,%s,%s,%s,%s,%s,%s,&silecsData.data.SilecsHeader_hdrBlk.device[0]._date);
SILECS_set_dt(${dt6},${dt5},${dt4},${dt3},${dt2},${dt1},${dt0},&silecsData.data.SilecsHeader_hdrBlk.device[0]._date);
}
"""
""")
#=========================================================================
# Example generation
......@@ -270,19 +261,19 @@ instantiationExample = """/*
* Automatically generated Addressing example
*"""
deviceModeDeviceListExample = """
* This example shows how to address the register %s of block %s
* of device %s of the class %s
deviceModeDeviceListExample = string.Template("""
* This example shows how to address the register ${registerName} of block ${blockName}
* of device ${deviceLabel} of the class ${className}
*
* silecsData.%s_%s.%s.%s = ....;
*/"""
* silecsData.${className}_${deviceLabel}.${blockName}.${registerName} = ....;
*/""")
blockModeDeviceListExample = """
* This example shows how to address the register %s of block %s
* of device %s of the class %s
blockModeDeviceListExample = string.Template("""
* This example shows how to address the register ${registerName} of block ${blockName}
* of device ${deviceLabel} of the class ${className}
*
* silecsData.%s_%s.%s.%s = ....;
*/"""
* silecsData.${className}_${blockName}.${deviceLabel}.${registerName} = ....;
*/""")
#=========================================================================
# C code generation Sub-function
......@@ -290,33 +281,33 @@ blockModeDeviceListExample = """
# HEADER file ------------------------------------------------------------
def cHeader(silecsRelease, baseAddress):
return header %(silecsRelease, baseAddress)
return header.substitute(silecsRelease=silecsRelease, baseAddress=baseAddress)
# REGISTER definition ----------------------------------------------------
def cRegister(regName, regFormat, regDim, regDim2=1, strLen=1):
if strLen > 1: # if it's a string
if regDim2 > 1: # add another pair of brackets to hold the string length in case second dimension of array is set
return stringArray %(whichDIGIFormat[regFormat], regName, regDim, regDim2, strLen)
return stringArray.substitute(regFormat=whichDIGIFormat[regFormat], regName=regName, regDim=regDim, regDim2=regDim2, strLen=strLen)
elif regDim > 1: # reuse the array2D syntax
return regArray2d %(whichDIGIFormat[regFormat], regName, regDim, strLen)
return regArray2d.substitute(regFormat=whichDIGIFormat[regFormat], regName=regName, regDim=regDim, strLen=strLen)
else: # regular string register
return regArray %(whichDIGIFormat[regFormat], regName, strLen)
return regArray.substitute(regFormat=whichDIGIFormat[regFormat], regName=regName, strLen=strLen)
else:
if regDim == 1 and regDim2 == 1: # scalar
return regScalar %(whichDIGIFormat[regFormat], regName)
return regScalar.substitute(regFormat=whichDIGIFormat[regFormat], regName=regName)
elif regDim > 1 and regDim2 == 1: # array
return regArray %(whichDIGIFormat[regFormat], regName, regDim)
return regArray.substitute(regFormat=whichDIGIFormat[regFormat], regName=regName, strLen=regDim)
else: # dim1>=1 and for whatever dim2, use double array syntax
return regArray2d %(whichDIGIFormat[regFormat], regName, regDim, regDim2)
return regArray2d.substitute(regFormat=whichDIGIFormat[regFormat], regName=regName, regDim=regDim, strLen=regDim2)
def cFirstBlockUDT(className, classVersion):
return firstBlockUDT %(className, classVersion)
return firstBlockUDT.substitute(className=className, classVersion=classVersion)
# BLOCK type definition (UDT) --------------------------------------------
def cBlockUDT(className, blockName, regList):
return blockUDT %(regList, className, blockName)
return blockUDT.substitute(regList=regList, className=className, blockName=blockName)
#============= DEVICE instantiation =============
......@@ -324,46 +315,46 @@ def cBlockUDT(className, blockName, regList):
# GLOBAL instantiation ----------------------------------------------
def cAllocationComment(plcProtocol):
return allocationComment %(plcProtocol)
return allocationComment.substitute(plcProtocol=plcProtocol)
# DEVICE MODE instantiation ----------------------------------------------
def cDeviceModeBlockInstantiation(className, blockName):
return deviceModeBlockInstantiation %(className, blockName, blockName)
return deviceModeBlockInstantiation.substitute(className=className, blockName=blockName)
def cDeviceModeClass_deviceNumber(className, blockList):
return deviceModeClass_deviceNumber %(blockList, className, className)
return deviceModeClass_deviceNumber.substitute(blockList=blockList, className=className)
def cDeviceModeClass_deviceList(blockList, deviceList):
return deviceModeClass_deviceList %(blockList, deviceList)
return deviceModeClass_deviceList.substitute(blockList=blockList, deviceList=deviceList)
def cDeviceModeDataInstantiation(classList):
return deviceMode_dataInstantiation %(classList)
return deviceMode_dataInstantiation.substitute(classList=classList)
# BLOCK MODE instantiation ----------------------------------------------
def cBlockModeDeviceInstantiation_deviceList(className, blockName, deviceName):
return blockModeDeviceInstantiation_deviceList %(className, blockName, deviceName)
return blockModeDeviceInstantiation_deviceList.substitute(className=className, blockName=blockName, deviceName=deviceName)
def cBlockModeBlockInstantiation(deviceList, className, blockName):
return blockModeBlockInstantiation %(deviceList, className, blockName)
return blockModeBlockInstantiation.substitute(deviceList=deviceList, className=className, blockName=blockName)
def cBlockModeDataInstantiation(classList):
return blockMode_dataInstantiation %(classList)
return blockMode_dataInstantiation.substitute(classList=classList)
# Init Function ----------------------------------------------
def cInitDeviceMode(ieVersion,ieChecks,ieUser):
dt = time.localtime(time.time())
return initFunctionDeviceMode %("SILECS_"+ieVersion,ieChecks,ieUser,dt[6],dt[5],dt[4],dt[3],dt[2],dt[1],dt[0])
return initFunctionDeviceMode.substitute(version="SILECS_"+ieVersion, checksum=ieChecks, user=ieUser, dt6=dt[6], dt5=dt[5], dt4=dt[4], dt3=dt[3], dt2=dt[2], dt1=dt[1], dt0=dt[0])
def cInitBlockMode(ieVersion,ieChecks,ieUser):
dt = time.localtime(time.time())
return initFunctionBlockMode %("SILECS_"+ieVersion,ieChecks,ieUser,dt[6],dt[5],dt[4],dt[3],dt[2],dt[1],dt[0])
return initFunctionBlockMode.substitute(version="SILECS_"+ieVersion, checksum=ieChecks, user=ieUser, dt6=dt[6], dt5=dt[5], dt4=dt[4], dt3=dt[3], dt2=dt[2], dt1=dt[1], dt0=dt[0])
# Example function ----------------------------------------------
def cExample(plcProtocol,className, blockName, registerName, deviceLabel =''):
if plcProtocol == 'DEVICE_MODE':
return instantiationExample + deviceModeDeviceListExample %(registerName, blockName, deviceLabel, className, className, deviceLabel, blockName, registerName)
return instantiationExample + deviceModeDeviceListExample.substitute(registerName=registerName, blockName=blockName, deviceLabel=deviceLabel, className=className)
else:
return instantiationExample + blockModeDeviceListExample %(registerName, blockName, deviceLabel, className, className, blockName, deviceLabel, registerName)
return instantiationExample + blockModeDeviceListExample.substitute(registerName=registerName, blockName=blockName, deviceLabel=deviceLabel, className=className)
......@@ -33,9 +33,9 @@ from iecommon import *
# Generates two Makefile.specific, one for the class design
# and another for the deploy unit
#-------------------------------------------------------------------------
def genMakefileClass(projectPath, centralMakefilePath, logTopics={'errorlog': True} ):
def genMakefileClass(projectPath, logTopics={'errorlog': True} ):
# Generate makefile for class design
source = fesaTemplates.genMakeDesign(centralMakefilePath)
source = fesaTemplates.genMakeDesign()
makefile = projectPath + "/" + "Makefile.specific"
if os.path.isfile(makefile): #dont overwrite
return
......@@ -44,9 +44,9 @@ def genMakefileClass(projectPath, centralMakefilePath, logTopics={'errorlog': Tr
fdesc.write(source)
fdesc.close()
def genMakefileDU(projectPath, silecsBasePath, snap7BasePath, logTopics={'errorlog': True}):
def genMakefileDU(projectPath, logTopics={'errorlog': True}):
# Generate makefile for class design
source = fesaTemplates.genMakeDeploy(silecsBasePath,snap7BasePath)
source = fesaTemplates.genMakeDeploy()
# Write file and save
makefile = projectPath + "/" + "Makefile.specific"
if os.path.isfile(makefile): #dont overwrite
......
......@@ -87,8 +87,29 @@ class FESADesignGenerator3_0_0(object):
fillAttributes(scalarNode, {'type': register.getFesaType()})
return scalarNode
def getOrCreateType(self,fieldNode,register):
if register.valueType == "scalar":
def getOrCreateCustomScalarType(self, fieldNode, register):
scalarNode = getOrCreateChildElement(fieldNode,'custom-type-scalar')
fillAttributes(scalarNode, {'data-type-name-ref': register.custom_type_name})
return scalarNode
def getOrCreateCustomArrayType(self,fieldNode,register):
arrayNode = getOrCreateChildElement(fieldNode,'custom-type-array')
fillAttributes(arrayNode, {'data-type-name-ref': register.custom_type_name})
dimNode = getOrCreateChildElement(arrayNode,'dim')
dimNode.setContent(str(register.dim1))
return arrayNode
def getOrCreateCustom2DArrayType(self,fieldNode,register):
array2DNode = getOrCreateChildElement(fieldNode,'custom-type-array2D')
fillAttributes(array2DNode, {'data-type-name-ref': register.custom_type_name})
dim1Node = getOrCreateChildElement(array2DNode,'dim1')
dim2Node = getOrCreateChildElement(array2DNode,'dim2')
dim1Node.setContent(str(register.dim1))
dim2Node.setContent(str(register.dim2))
return array2DNode
def getOrCreateType(self,fieldNode, register):
if register.valueType == "scalar":
return self.getOrCreateScalarType(fieldNode,register)
elif register.valueType == "array":
return self.getOrCreateArrayType(fieldNode,register)
......@@ -100,6 +121,12 @@ class FESADesignGenerator3_0_0(object):
return self.getOrCreateStringArrayType(fieldNode,register)
elif register.valueType == "stringArray2D":
iecommon.logError('ERROR: In register '+register.name+' - 2D array of strings not supported in FESA.', True, {'errorlog': True})
elif register.valueType == "custom-type-scalar":
return self.getOrCreateCustomScalarType(fieldNode, register)
elif register.valueType == "custom-type-array":
return self.getOrCreateCustomArrayType(fieldNode, register)
elif register.valueType == "custom-type-array2D":
return self.getOrCreateCustom2DArrayType(fieldNode, register)
else:
iecommon.logError('ERROR: Unknown data-type:' + register.valueType, True, {'errorlog': True})
return None
......
......@@ -51,11 +51,11 @@ def genHSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
for block in designClass.getDesignFesaBlocks():
if block.isAcquisition():
source += fesaTemplates.genHBlock('RO', block.name,block.getFesaName() )
source += fesaTemplates.genHReadBlock(className, block.name)
elif block.isCommand() or block.isConfiguration():
source += fesaTemplates.genHBlock('WO', block.name,block.getFesaName())
source += fesaTemplates.genHWriteBlock(className, block.name, block.getFesaName())
else: # Setting
source += fesaTemplates.genHBlock('RW', block.name,block.getFesaName())
source += fesaTemplates.genHReadWriteBlock(className, block.name, block.getFesaName())
source += fesaTemplates.genHBottom(className)
......@@ -106,7 +106,7 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
for block in blockList:
if block.isSetting() or block.isAcquisition(): #configuration-fields in fesa are not writable, so no setter to generate for them !
finalSource += fesaTemplates.genCCommonGet(block.name,className)
finalSource += fesaTemplates.genCCommonGet(block.name, className)
finalSource += '\n'
finalSource += fesaTemplates.cRecv
for register in block.getDesignRegisters():
......@@ -122,10 +122,43 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
finalSource += fesaTemplates.genCGetArrayReg(register)
elif register.valueType == 'array2D':
finalSource += fesaTemplates.genCGetArray2DReg(register)
elif register.valueType == 'custom-type-scalar':
finalSource += fesaTemplates.genCGetCustomScalarReg(register)
elif register.valueType == 'custom-type-array':
finalSource += fesaTemplates.genCGetCustomArrayReg(register)
elif register.valueType == 'custom-type-array2D':
finalSource += fesaTemplates.genCGetCustomArray2DReg(register)
finalSource += '\n }\n'
if block.isSetting():
finalSource += fesaTemplates.genCDatatypeGet(block.name, block.getFesaName(), className)
finalSource += '\n'
finalSource += fesaTemplates.cRecv
for register in block.getDesignRegisters():
if register.valueType == 'string':
finalSource += fesaTemplates.genCGetStringRegData(register)
elif register.valueType == 'stringArray':
finalSource += fesaTemplates.genCGetStringArrayRegData(register)
elif register.valueType == 'stringArray2D':
iecommon.logError('ERROR: In register '+register.name+' - 2D array of strings not supported in FESA.', True, {'errorlog': True})
elif register.valueType == 'scalar':
finalSource += fesaTemplates.genCGetScalarRegData(register)
elif register.valueType == 'array':
finalSource += fesaTemplates.genCGetArrayRegData(register)
elif register.valueType == 'array2D':
finalSource += fesaTemplates.genCGetArray2DRegData(register)
elif register.valueType == 'custom-type-scalar':
finalSource += fesaTemplates.genCGetCustomScalarRegData(register)
elif register.valueType == 'custom-type-array':
finalSource += fesaTemplates.genCGetCustomArrayRegData(register)
elif register.valueType == 'custom-type-array2D':
finalSource += fesaTemplates.genCGetCustomArray2DRegData(register)
finalSource += '\n }\n'
if block.isWritable():
finalSource += fesaTemplates.genCCommonSet(block.name,className)
finalSource += fesaTemplates.genCCommonSet(block.name, className)
for register in block.getDesignRegisters():
if register.valueType == 'string':
finalSource += fesaTemplates.genCSetStringReg(register)
......@@ -139,6 +172,12 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
finalSource += fesaTemplates.genCSetArrayReg(register)
elif register.valueType == 'array2D':
finalSource += fesaTemplates.genCSetArray2DReg(register)
elif register.valueType == 'custom-type-scalar':
finalSource += fesaTemplates.genCSetScalarReg(register)
elif register.valueType == 'custom-type-array':
finalSource += fesaTemplates.genCSetCustomArrayReg(register)
elif register.valueType == 'custom-type-array2D':
finalSource += fesaTemplates.genCSetCustomArray2DReg(register)
finalSource += fesaTemplates.cSend
finalSource += ' }\n'
finalSource += fesaTemplates.genCDatatypeSet(block.name,block.getFesaName(), className)
......@@ -156,6 +195,12 @@ def genCppSource(className, silecsRoot, fesaRoot, sourcePath,logTopics):
finalSource += fesaTemplates.genCSetArrayRegData(register)
elif register.valueType == 'scalar':
finalSource += fesaTemplates.genCSetScalarRegData(register)
elif register.valueType == 'custom-type-scalar':
finalSource += fesaTemplates.genCSetScalarRegData(register)
elif register.valueType == 'custom-type-array':
finalSource += fesaTemplates.genCSetCustomArrayRegData(register)
elif register.valueType == 'custom-type-array2D':
finalSource += fesaTemplates.genCSetCustomArray2DRegData(register)
finalSource += fesaTemplates.cSend
finalSource += '\n }\n' # closing bracket for block
......
......@@ -16,11 +16,11 @@
import fesa.fesa_3_0_0.generateBuildEnvironment
def genMakefileClass(projectPath, centralMakefilePath, logTopics={'errorlog': True} ):
return fesa.fesa_3_0_0.generateBuildEnvironment.genMakefileClass(projectPath, centralMakefilePath, logTopics )
def genMakefileClass(projectPath, logTopics={'errorlog': True} ):
return fesa.fesa_3_0_0.generateBuildEnvironment.genMakefileClass(projectPath, logTopics )
def genMakefileDU(projectPath, silecsBasePath, snap7BasePath, logTopics={'errorlog': True}):
return fesa.fesa_3_0_0.generateBuildEnvironment.genMakefileDU(projectPath, silecsBasePath, snap7BasePath, logTopics )
def genMakefileDU(projectPath, logTopics={'errorlog': True}):
return fesa.fesa_3_0_0.generateBuildEnvironment.genMakefileDU(projectPath, logTopics )
def genCProjectFile(projectPath, logTopics={'errorlog': True}):
return fesa.fesa_3_0_0.generateBuildEnvironment.genCProjectFile(projectPath, logTopics )
......@@ -16,11 +16,11 @@
import fesa.fesa_3_1_0.generateBuildEnvironment
def genMakefileClass(projectPath, centralMakefilePath, logTopics={'errorlog': True} ):
return fesa.fesa_3_1_0.generateBuildEnvironment.genMakefileClass(projectPath, centralMakefilePath, logTopics )
def genMakefileClass(projectPath, logTopics={'errorlog': True} ):
return fesa.fesa_3_1_0.generateBuildEnvironment.genMakefileClass(projectPath, logTopics )
def genMakefileDU(projectPath, silecsBasePath, snap7BasePath, logTopics={'errorlog': True}):
return fesa.fesa_3_1_0.generateBuildEnvironment.genMakefileDU(projectPath, silecsBasePath, snap7BasePath, logTopics )
def genMakefileDU(projectPath, logTopics={'errorlog': True}):
return fesa.fesa_3_1_0.generateBuildEnvironment.genMakefileDU(projectPath, logTopics )
def genCProjectFile(projectPath, logTopics={'errorlog': True}):
return fesa.fesa_3_1_0.generateBuildEnvironment.genCProjectFile(projectPath, logTopics )
......@@ -96,3 +96,42 @@ class FESADesignGenerator7_3_0(fesa.fesa_3_1_0.generateFesaDesign.FESADesignGene
else:
globalConfigurationNode = globalDataNode.xpathEval("configuration")[0]
self.getOrCreatePLCClassVersionField(globalConfigurationNode,designClass.version)
# Fill custom-types with enums.
enums = designClass.getEnums()
customTypesNode = doc.xpathEval('/equipment-model/custom-types')[0]
for enum in enums:
enumElement = getOrCreateNamedChildElement(customTypesNode, "enum", enum.name)
existing_items = enumElement.xpathEval("item")
to_add = []
to_edit = []
# Loop through all new enums and check which should be added and which edited.
for item in enum.items:
if item.symbol in [item.prop("symbol") for item in existing_items]:
to_edit.append(item)
else:
to_add.append(item)
# Loop though all existing enums and remove any which are not in new enums.
to_add_edit = to_add + to_edit
for existing in existing_items:
if existing.prop("symbol") not in [item.symbol for item in to_add_edit]:
existing.unlinkNode()
existing.freeNode()
# Edit existing items.
for item in to_edit:
for existing in existing_items:
if existing.prop("symbol") != item.symbol:
continue
existing.setProp("access", item.access)
existing.setProp("value", item.value)
# Add missing items.
for item in to_add:
itemElement = libxml2.newNode("item")
enumElement.addChild(itemElement)
itemElement.setProp("access", item.access)
itemElement.setProp("value", item.value)
itemElement.setProp("symbol", item.symbol)
......@@ -43,13 +43,13 @@ def genClassHeader(workspacePath, deploy, design, funcGetSilecsDesignFilePath, f
classDeclarations += genduwrappertemplate.getBlockClass(block,registerInitializerList,registerGetterSetter,registersDimentionsDeclaration,registersDeclaration)
blockGetters = ""
sendRecvBlocks = genduwrappertemplate.getDeviceSendRecvBlocks(designClass.getBlockNodes())
sendRecvBlocks = genduwrappertemplate.getDeviceSendRecvBlocks(designClass.getBlockNodes(), designClass.customTypesXmlNode)
for block in designClass.getDesignBlocks():
blockGetters += genduwrappertemplate.getDeviceBlockGetterSetter(block)
classDeclarations += genduwrappertemplate.getDeviceClass(blockGetters,sendRecvBlocks)
sendRecvBlocks = genduwrappertemplate.getControllerSendRecvBlocks(designClass.getBlockNodes())
sendRecvBlocks = genduwrappertemplate.getControllerSendRecvBlocks(designClass.getBlockNodes(), designClass.customTypesXmlNode)
classDeclarations = genduwrappertemplate.getDesignClass(design.name, design.version)
designWrapper = genduwrappertemplate.designFileTemplate.substitute({'designNameCapitalized' : iecommon.capitalizeString(design.name),'designNameUpper' : design.name.upper(),'classDeclarations' : classDeclarations})
......
......@@ -337,10 +337,10 @@ controllerSendTemplate = string.Template("""\
""")
def getControllerSendRecvBlocks(blockList):
def getControllerSendRecvBlocks(blockList, customTypesXmlNode):
text = ""
for blockNode in blockList:
block = DesignBlock(blockNode)
block = DesignBlock(blockNode, customTypesXmlNode)
map = {'blockName' : block.name,
'blockNameCapitalized' : block.getNameCapitalized()}
if block.isReadable():
......@@ -405,39 +405,39 @@ ${blockInitialization}\
matrixRegisterAssignementGetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1 * block.${regName}Dim2];
getSilecsDevice()->getRegister("${regName}")->getVal${silecsTypeCapitalized}Array2D(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
getSilecsDevice()->getRegister("${regName}")->getValArray2D<${cType}>(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
block.set${regNameCapitalized}(__${regName});
delete[] __${regName};
""")
arrayRegisterAssignementGetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1];
getSilecsDevice()->getRegister("${regName}")->getVal${silecsTypeCapitalized}Array(__${regName}, block.${regName}Dim1);
getSilecsDevice()->getRegister("${regName}")->getValArray<${cType}>(__${regName}, block.${regName}Dim1);
block.set${regNameCapitalized}(__${regName});
delete[] __${regName};
""")
scalarRegisterAssignementGetter = string.Template("""\
// Copy register ${regName}
block.set${regNameCapitalized}(getSilecsDevice()->getRegister("${regName}")->getVal${silecsTypeCapitalized}());
block.set${regNameCapitalized}(getSilecsDevice()->getRegister("${regName}")->getVal<${cType}>());
""")
matrixRegisterAssignementSetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1 * block.${regName}Dim2];
block.get${regNameCapitalized}(__${regName});
getSilecsDevice()->getRegister("${regName}")->setVal${silecsTypeCapitalized}Array2D(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
getSilecsDevice()->getRegister("${regName}")->setValArray2D<${cType}>(__${regName}, block.${regName}Dim1, block.${regName}Dim2);
delete[] __${regName};
""")
arrayRegisterAssignementSetter = string.Template("""\
// Copy register ${regName}
${cType} *__${regName} = new ${cType}[block.${regName}Dim1];
block.get${regNameCapitalized}(__${regName});
getSilecsDevice()->getRegister("${regName}")->setVal${silecsTypeCapitalized}Array(__${regName}, block.${regName}Dim1);
getSilecsDevice()->getRegister("${regName}")->setValArray<${cType}>(__${regName}, block.${regName}Dim1);
delete[] __${regName};
""")
scalarRegisterAssignementSetter = string.Template("""\
// Copy register ${regName}
getSilecsDevice()->getRegister("${regName}")->setVal${silecsTypeCapitalized}(block.get${regNameCapitalized}());
getSilecsDevice()->getRegister("${regName}")->setVal<${cType}>(block.get${regNameCapitalized}());
""")
def getDeviceBlockGetterSetter(block):
......@@ -447,7 +447,6 @@ def getDeviceBlockGetterSetter(block):
for register in block.getDesignRegisters():
map = {'regName' : register.name,
'regNameCapitalized' : register.getNameCapitalized(),
'silecsTypeCapitalized' : register.getSilecsTypeCapitalized(),
'cType' : register.getCType()}
if register.isArray2D():
blockInitialization += matrixRegisterAssignementGetter.substitute(map)
......@@ -465,7 +464,6 @@ def getDeviceBlockGetterSetter(block):
for register in block.getDesignRegisters():
map = {'regName' : register.name,
'regNameCapitalized' : register.getNameCapitalized(),
'silecsTypeCapitalized' : register.getSilecsTypeCapitalized(),
'cType' : register.getCType()}
if register.isArray2D():
blockInitialization += matrixRegisterAssignementSetter.substitute(map)
......@@ -502,10 +500,10 @@ deviceSendTemplate = string.Template("""\
""")
def getDeviceSendRecvBlocks(blockList):
def getDeviceSendRecvBlocks(blockList, customTypesXmlNode):
text = ""
for blockNode in blockList:
block = DesignBlock(blockNode)
block = DesignBlock(blockNode, customTypesXmlNode)
map = {'blockName' : block.name,
'blockNameCapitalized' : block.getNameCapitalized()}
if block.isReadable():
......
......@@ -36,6 +36,9 @@ from model.Deploy.Controller import *
import model.Deploy.Device
from model.Param.Param import *
# maximum number of boolean bits in the same register address
MAX_BITS_IN_ADDRESS = 8
#-------------------------------------------------------------------------
# Trimming: remove new-line, tabulation and spaces of the string
# Used to compute CRC32 on significant data only
......@@ -123,8 +126,15 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
iecommon.logDebug("-----------------------------------------",logTopics)
iecommon.logDebug("------ Analysing Class " + deployDesign.name + " ------",logTopics)
iecommon.logDebug("-----------------------------------------",logTopics)
designDOM = iefiles.loadSilecsDesignDOM(workspacePath, deployDesign, silecsVersion, funcGetSilecsDesignFilePath)
if iecommon.supportsDateTimeLong(controller):
datetime_format = xmltemplate.DATE_TIME_LONG
else:
datetime_format = xmltemplate.DATE_AND_TIME
iecommon.logDebug("Datetime format is:", datetime_format)
designDOM = iefiles.loadSilecsDesignDOM(workspacePath, deployDesign, silecsVersion, funcGetSilecsDesignFilePath, datetime_format)
designClass = DesignClass.getDesignClassFromRootNode(designDOM)
paramClass = ParamClass()
paramClass.initWithDesignClass(designClass)
......@@ -151,9 +161,37 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
blockSize = 0 # block size (sum of the register size)
blockMemSize = 0 # block size (sum of the regist)
controller.regCounter = 0 # used for NI register address generation
# additional data for managing booleans
isBooleanRegister = False
bitNumber = 0
nextRegister = None
# INNER LOOP TO ACCESS AT REGISTER LEVEL
for designRegister in designBlock.getDesignRegisters():
designRegisters = designBlock.getDesignRegisters()
for i, designRegister in enumerate(designRegisters):
if i + 1 != len(designRegisters):
nextRegister = designRegisters[i+1]
else:
nextRegister = None
iecommon.logDebug("------ Processing Register " + designRegister.name + " ------",logTopics)
# Allow bool register only for Siemens controller
if designRegister.format == 'bool':
isBooleanRegister = True
if not designRegister.isScalar():
iecommon.logError("Invalid format in register: %s Bool format available only for scalars" %designRegister.name, False, logTopics)
sys.exit(2)
if controller.brand != 'SIEMENS':
iecommon.logError("Invalid format in register: %s. Bool format available only on SIEMENS controllers" %designRegister.name, False,logTopics)
sys.exit(2)
else:
isBooleanRegister = False
bitNumber = 0
# Set length attribute only for string registers
if designRegister.format == 'string': # TODO: Port this constraint to java
if controller.brand == 'DIGI':
......@@ -164,12 +202,25 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
sys.exit(2)
regAddress = controller.alignRegAddress(designBlock, designRegister, regAddress)
controller.regCounter += 1 # used for NI register address generation
# Set register mem-size
paramRegister = ParamRegister()
paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize)
# Compute address for the next register
regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
if isBooleanRegister:
paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize, bitNumber)
else:
paramRegister.initWithDesignRegister(designRegister,regAddress,controller.msize)
if isBooleanRegister and nextRegister and nextRegister.format == "bool":
bitNumber = (bitNumber + 1) % MAX_BITS_IN_ADDRESS
if bitNumber == 0:
# Compute address for the next register if max bits in address achieved
regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
else:
# Compute address for the next register
regAddress = controller.computeNextRegAddress(designBlock, designRegister, regAddress)
paramBlock.xmlNode.addChild(paramRegister.xmlNode)
#paramRegister.xmlNode.shellPrintNode()
#iterativelly compute the block size (accumulator initialized outside the loop)
......@@ -190,6 +241,7 @@ def genParamBase( funcGetSilecsDesignFilePath, funcGetParameterFile, funcGetSile
for device in devices:
instance = libxml2.newNode("Instance")
instance.setProp("label", device.silecsDeviceLabel)
instance.setProp("fesa-label", device.fesaDeviceName)
instance.setProp("address", str(int(controller.computeInstAddress(mappings[MEM_TYPE]))))
instance.setProp("DI-address", str(int(controller.computeInstAddress(mappings[DI_TYPE]))))
instance.setProp("DO-address", str(int(controller.computeInstAddress(mappings[DO_TYPE]))))
......