diff --git a/silecs-cli-client/examples/interactive.sh b/silecs-cli-client/examples/interactive.sh index a63b43bf397a97dcc68499c9efd0d84f7fca66a1..07dacc0147ea788e45e2f88dd2c1c513a98d5ec8 100755 --- a/silecs-cli-client/examples/interactive.sh +++ b/silecs-cli-client/examples/interactive.sh @@ -1,2 +1,23 @@ #!/bin/sh -set -e \ No newline at end of file +set -e + +CPU="x86_64" +SCRIPT=$(readlink -f "$0") +SCRIPTPATH=$(dirname "$SCRIPT") # path where this script is located in + +SNAP7_PATH=${SCRIPTPATH}/../../../git/snap7/snap7-full/build/bin/${CPU}-linux + +BINARY="${SCRIPTPATH}/../build/bin/${CPU}/silecs-cli-client" + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SNAP7_PATH +echo "##### LD_LIBRARY_PATH: ###################################################################################" +echo "$LD_LIBRARY_PATH" +echo "##########################################################################################################" +echo "" + +PARAM_FILE=/common/home/bel/schwinn/lnx/workspace-silecs-neon/SiemensTestDU/generated/client/tsts7001.silecsparam + +# config for SiemensTestDU +ARGUMENTS="-f $PARAM_FILE -i -c" +COMMAND="$BINARY $ARGUMENTS" +$COMMAND diff --git a/silecs-cli-client/src/silecs-cli-client/main.cpp b/silecs-cli-client/src/silecs-cli-client/main.cpp index 288a88549f87545e7e9425e4eab449d90b20ff8f..128774cf8f3b7418dbbd2fabaa0ca3fd546e0255 100755 --- a/silecs-cli-client/src/silecs-cli-client/main.cpp +++ b/silecs-cli-client/src/silecs-cli-client/main.cpp @@ -17,6 +17,7 @@ #include <stdint.h> #include <stdio.h> #include <getopt.h> +#include <termios.h>// getch #include <iostream> #include <iomanip> // std::right, etc #include <unistd.h>//usleep @@ -27,6 +28,8 @@ #include <silecs-communication/interface/equipment/SilecsDevice.h> #include <silecs-communication/interface/equipment/SilecsRegister.h> +#include <boost/assign/list_of.hpp> // init vector of strings + const std::string startbold = "\e[1m"; const std::string endbold = "\e[0m"; @@ -87,6 +90,53 @@ void printHelp() std::cout << std::endl; } +/* reads from keypress, doesn't echo */ +int getch(void) +{ + struct termios oldattr, newattr; + int ch; + tcgetattr( STDIN_FILENO, &oldattr ); + newattr = oldattr; + newattr.c_lflag &= ~( ICANON | ECHO ); + tcsetattr( STDIN_FILENO, TCSANOW, &newattr ); + ch = getchar(); + tcsetattr( STDIN_FILENO, TCSANOW, &oldattr ); + return ch; +} + +//returns -1 for "return to previous menu" or array-index of list +int querryUserPickFromList(std::vector<std::string> &list) +{ + int selection = -1; + std::vector<std::string>::iterator item; + while(selection < 0 || (unsigned int)selection > list.size()) + { + int itemIndex = 1; + std::cout << std::endl; + std::cout << "The following options are available:" << std::endl; + std::cout << std::endl; + for( item = list.begin();item!= list.end(); item++) + { + std::cout << std::setw(5) << std::right << itemIndex << ": " << std::setw(30) << std::left << *item << std::endl; + itemIndex++; + } + + std::cout << std::endl; + std::cout << std::setw(5) << std::right << "0" << ": " << std::setw(30) << std::left << "return to previous menu" << std::endl; + std::cout << std::endl; + std::cout << "Waiting for your selection "; + int input = getch(); + std::cout << std::endl; + selection = input - 48; //ascii magic + if( selection < 0 || (unsigned int)selection > list.size() ) + { + std::cout << "Invalid input: '" << input << "'. press any key to take another try!" << std::endl; + getch(); + } + } + return selection-1; // -1 = return ... everything else is array-index +} + std::string getPLCName(Silecs::XMLParser ¶mParser) { Silecs::ElementXML mappingNode = paramParser.getFirstElementFromXPath("/SILECS-Param/SILECS-Mapping"); @@ -115,6 +165,18 @@ bool isRegisterInBlock(std::string registerName, std::string blockName, Silecs: return false; } +std::vector<std::string> getDeviceNames(Silecs::XMLParser ¶mParser) +{ + 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++) + { + deviceNames.push_back(deviceNode->getAttribute("label")); + } + return deviceNames; +} + std::vector<std::string> getBlockNamesFromDeviceName(std::string deviceName, Silecs::XMLParser ¶mParser) { std::vector< std::string > blockNames; @@ -130,6 +192,21 @@ std::vector<std::string> getBlockNamesFromDeviceName(std::string deviceName, Si return blockNames; } +std::vector<std::string> getRegisterNamesFromDeviceBlockName(std::string deviceName, std::string blockName, Silecs::XMLParser ¶mParser) +{ + 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 + "']/Block[@name='"+ blockName + "']/Register"); + boost::ptr_vector<Silecs::ElementXML>::iterator registerNode; + for( registerNode = registerNodes.begin();registerNode!= registerNodes.end(); registerNode++) + { + //std::cout<< block->getAttribute("name") << std::endl; + registerNames.push_back(registerNode->getAttribute("name")); + } + return registerNames; +} + std::string getRegisterValueAsString(Silecs::Register* reg ) { std::ostringstream os; @@ -208,12 +285,123 @@ void setRegister(Silecs::Register* reg,std::string value) int connectInteractive(Silecs::Service *service, Silecs::XMLParser ¶mParser) { -//ask class -//ask device or print all devices -//ask select block or print device -//ask select register or print block -//ask get/set register - //Silecs::Cluster *cluster = NULL; + 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; + + while(true) + { + std::cout << std::setw(20) << std::right << "Device:" << std::setw(20) << std::left << startbold << arg_deviceName << endbold <<std::endl; + std::cout << std::setw(20) << std::right << "Block:" << std::setw(20) << std::left << startbold << arg_blockName << endbold <<std::endl; + std::cout << std::setw(20) << std::right << "Register:" << std::setw(20) << std::left << startbold << arg_registerName << endbold <<std::endl; + int item = querryUserPickFromList(mainMenu); + switch(item) + { + case -1: + return EXIT_SUCCESS; + case 0: + { + std::vector<std::string> devices = getDeviceNames(paramParser); + int index = querryUserPickFromList(devices); + if( index == -1 ) + break; + 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); + break; + } + case 1: + { + if(arg_deviceName.empty()) + { + std::cout << "Please select a device first! - Press any key to continue." << std::endl; + getch(); + break; + } + std::vector<std::string> blocks = getBlockNamesFromDeviceName(arg_deviceName,paramParser); + int index = querryUserPickFromList(blocks); + if( index == -1 ) + break; + arg_blockName = blocks[index]; + arg_registerName = ""; + break; + } + case 2: + { + if(arg_deviceName.empty() || arg_blockName.empty() ) + { + std::cout << "Please connect to a device first! - Press any key to continue." << std::endl; + getch(); + break; + } + std::vector<std::string> registers = getRegisterNamesFromDeviceBlockName(arg_deviceName,arg_blockName,paramParser); + int index = querryUserPickFromList(registers); + 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; + getch(); + break; + } + printTableHead(); + 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; + getch(); + break; + } + printTableHead(); + 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; + getch(); + break; + } + printTableHead(); + printRegister(device,reg ); + break; + case 6: + if(arg_deviceName.empty()) + { + std::cout << "Please connect to a device first! - Press any key to continue." << std::endl; + getch(); + break; + } + 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(); + break; + default: + std::cout << "Invalid option:" << item << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; } @@ -227,7 +415,11 @@ int connectNonInteractive(Silecs::Service *service, Silecs::XMLParser ¶mPars std::cout << "Error: Failed to connect to PLC."<< std::endl; return EXIT_FAILURE; } - printRunState(plc); + + if( !arg_silent ) + { + printRunState(plc); + } Silecs::Device *device = plc->getDevice(arg_deviceName); Silecs::Register* reg = NULL; @@ -350,8 +542,6 @@ int main(int argc, char **argv) return EXIT_FAILURE; } interactiveOptionSet = true; - std::cout << "TODO: implement interactive-mode" << std::endl; - return EXIT_FAILURE; break; case 's': arg_silent = true;