blob: 5b6889ececed1ce47bc78a2b128eef928aa5c8c7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 fortiss GmbH
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Alois Zoitl - initial API and implementation and/or initial documentation
* Jose Cabral - expansion of capabilities
*******************************************************************************/
#include "processinterface.h"
#include <sstream>
#include <linux/input.h>
#include <fcntl.h>
const std::string CLMSEV3ProcessInterface::scmLEDID("led");
const std::string CLMSEV3ProcessInterface::scmSensorID("sensor");
const std::string CLMSEV3ProcessInterface::scmSensorWID("sensorw");
const std::string CLMSEV3ProcessInterface::scmButtonID("button");
const std::string CLMSEV3ProcessInterface::scmMotorID("motor");
const std::string CLMSEV3ProcessInterface::scmModeID("mode");
const std::string CLMSEV3ProcessInterface::scmEnableID("enable");
const std::string CLMSEV3ProcessInterface::scmResetID("reset");
const std::string CLMSEV3ProcessInterface::scmPWMID("pwm");
const std::string CLMSEV3ProcessInterface::scmSPEEDID("speed");
const std::string CLMSEV3ProcessInterface::scmStopID("stop");
const std::string CLMSEV3ProcessInterface::scmPositionID("position");
const std::string CLMSEV3ProcessInterface::scmRotID("rot");
const char * const CLMSEV3ProcessInterface::scmOK = "OK";
const char * const CLMSEV3ProcessInterface::scmNotInitialised = "Not initialized";
const char * const CLMSEV3ProcessInterface::scmCouldNotRead = "Could not read";
const char * const CLMSEV3ProcessInterface::scmCouldNotWrite = "Could not write";
//TODO: QO is not changed in this file, should be changed according to the execution?
CLMSEV3ProcessInterface::CLMSEV3ProcessInterface(CResource *paSrcRes, const SFBInterfaceSpec *paInterfaceSpec, const CStringDictionary::TStringId paInstanceNameId, TForteByte *paFBConnData, TForteByte *paFBVarsData) :
CProcessInterfaceBase(paSrcRes, paInterfaceSpec, paInstanceNameId, paFBConnData, paFBVarsData){
mFile.rdbuf()->pubsetbuf(0, 0); //disable buffer to avoid latency
mnTypeOfIO = UNDEFINED;
mstButtonVariables = 0;
mnNoOfBits = 0;
}
CLMSEV3ProcessInterface::~CLMSEV3ProcessInterface(){
if (0 != mstButtonVariables){
delete mstButtonVariables;
}
}
bool CLMSEV3ProcessInterface::initialise(bool paIsInput){
bool retVal = false;
std::vector<std::string> paramsList(generateParameterList());
STATUS() = scmNotInitialised;
if (0 != paramsList.size()){
std::string ioType = paramsList[1];
if(scmLEDID == ioType){ // led parameter format = emb.led.[right | left].[red | green]
retVal = setupLED(paramsList, paIsInput);
}else if(scmSensorID == ioType){ // boolean sensor parameter format = in[X].sensor.[Y]
retVal = setupSensor(paramsList, paIsInput);
}
else if(scmSensorWID == ioType){ //word sensor parameter format = in[X].sensorw.[Y]
retVal = setupSensorW(paramsList, paIsInput);
}
else if(scmButtonID == ioType){ //button parameter format = emb.button.[UP | DOWN | LEFT | RIGHT | ENTER | BACKSPACE]
retVal = setupButton(paramsList[2], paIsInput);
}
else if(scmMotorID == ioType){ //pwm parameter format = out[X].motor.[]
retVal = setupMotor(paramsList, paIsInput);
}
}
if (true == retVal){
STATUS() = scmOK;
}else{
DEVLOG_ERROR("The FB with PARAMS() = '%s' couldn't be initialized. PARAMS is not well defined.", PARAMS().getValue());
}
return retVal;
}
bool CLMSEV3ProcessInterface::deinitialise(){
if(mFile.is_open()){
mFile.close();
}
if (0 != mstButtonVariables){
delete mstButtonVariables;
}
STATUS() = scmOK;
return true;
}
/* READ AND WRITE FUNCTIONS */
bool CLMSEV3ProcessInterface::readPin(){
bool retVal = false;
if(mFile.is_open() || (0 != mstButtonVariables && -1 != mstButtonVariables->nmFd)){
switch (mnTypeOfIO){
case SENSOR: {
char binData = 0;
mFile.clear();
mFile.seekg(0, std::ios::beg);
mFile.read(&binData, 1);
if (mFile.fail()){
STATUS() = scmCouldNotRead;
}else{
IN_X() = ('0' != binData) ? true : false;
STATUS() = scmOK;
retVal = true;
}
break;
}
case BUTTON: {
unsigned char buf[96];
for(int i = 0; i < 96; i++){
buf[i] = 0;
}
if(0 < ioctl(mstButtonVariables->nmFd, EVIOCGKEY(96), buf)){
IN_X() = !(buf[mstButtonVariables->nmbutton / 8] & (1 << (mstButtonVariables->nmbutton % 8))) ? true : false;
STATUS() = scmOK;
retVal = true;
}else{
STATUS() = scmCouldNotRead;
}
break;
}
default:{
STATUS() = scmCouldNotRead;
break;
}
}
}else{
STATUS() = scmCouldNotRead;
}
if (!retVal){
DEVLOG_ERROR("Reading pin failed. The FB with PARAMS() = '%s' couldn't be read.", PARAMS().getValue());
}
return retVal;
}
bool CLMSEV3ProcessInterface::writePin(){
bool retVal = false;
if(mFile.is_open()){
mFile.clear();
mFile.seekp(0, std::ios::beg);
switch (mnTypeOfIO){
case LED:{
unsigned int val = (false != OUT_X()) ? 255 : 0; //if true set the led to full glowing
mFile << val;
break;
}
case MOTOR_ENABLE:{
std::string val = (false != OUT_X()) ? "run-direct" : "stop";
mFile << val;
break;
}
case MOTOR_RESET:{
if (true == OUT_X()){
mFile << "reset";
}
break;
}
default:{
STATUS() = scmCouldNotWrite;
break;
}
}
if (mFile.fail()){
STATUS() = scmCouldNotWrite;
}else{
STATUS() = scmOK;
retVal = true;
}
}else{
STATUS() = scmCouldNotWrite;
}
if (!retVal){
DEVLOG_ERROR("Writing pin failed. The FB with PARAMS() = '%s' couldn't be written.", PARAMS().getValue());
}
return retVal;
}
bool CLMSEV3ProcessInterface::readWord(){
bool retVal = false;
if (SENSORW_VALUE == mnTypeOfIO || MOTOR_PWM == mnTypeOfIO || MOTOR_SPEED == mnTypeOfIO || MOTOR_ROT == mnTypeOfIO) {
TForteInt32 val;
int internalChecker;
internalChecker = readNumberFromFile(&val);
if (0 == internalChecker){
IN_W() = (TForteWord) (val);
STATUS() = scmOK;
retVal = true;
}else if (1 == internalChecker){
STATUS() = scmCouldNotRead;
DEVLOG_ERROR("Reading word failed. The FB with PARAMS() = '%s' is not initialized.", PARAMS().getValue());
}else{
STATUS() = scmCouldNotRead;
}
}else if (SENSORW_MODE == mnTypeOfIO){
if (mFile.is_open()){
std::string mode;
mFile.clear();
mFile.seekg(0, std::ios::beg);
std::getline(mFile, mode);
if(mFile.fail()){
STATUS() == scmCouldNotRead;
}else{
TForteWord counter = 0;
std::vector<std::string>::iterator it;
for (it = mModes.begin(); it < mModes.end(); ++it, counter++){
if (mode == *it){
IN_W() = counter;
STATUS() = scmOK;
retVal = true;
break;
}
}
if (it == mModes.end()){
STATUS() == scmCouldNotRead;
}
}
}else{
STATUS() = scmCouldNotRead;
}
}else{
STATUS() = scmCouldNotRead;
}
if (!retVal){
DEVLOG_ERROR("Reading word failed. The FB with PARAMS() = '%s' couldn't be read.", PARAMS().getValue());
}
return retVal;
}
bool CLMSEV3ProcessInterface::writeWord(){
bool retVal = false;
if (mFile.is_open()){
bool writeAttempted = false;
mFile.clear();
mFile.seekp(0, std::ios::beg);
TForteWord val = OUT_W();
switch (mnTypeOfIO){
case MOTOR_PWM:{
TForteInt16 finalVal = (TForteInt16) val;
if (-100 <= finalVal && 100 >= finalVal){
mFile << finalVal;
writeAttempted = true;
}else{
STATUS() = scmCouldNotWrite;
}
break;
}
case MOTOR_STOP:{
std::string finalVal = "";
switch(val){
case 0:{
finalVal = "coast";
break;
}
case 1:{
finalVal = "brake";
break;
}
case 2:{
finalVal = "hold";
break;
}
default:{
STATUS() = scmCouldNotWrite;
break;
}
}
if ("" != finalVal){
mFile << finalVal;
writeAttempted = true;
}
break;
}
case SENSORW_MODE:{
std::string finalVal = "";
if (val < mModes.size()){
finalVal = mModes[val];
mFile << finalVal;
writeAttempted = true;
}else{
STATUS() = scmCouldNotWrite;
}
break;
}
default:{
STATUS() = scmCouldNotWrite;
break;
}
}
if (!mFile.fail() && writeAttempted) {
STATUS() = scmOK;
retVal = true;
}else{
STATUS() = scmCouldNotWrite;
}
}else{
STATUS() = scmNotInitialised;
}
if (!retVal){
DEVLOG_ERROR("Writing word failed. The FB with PARAMS() = '%s' couldn't be written.", PARAMS().getValue());
}
return retVal;
}
bool CLMSEV3ProcessInterface::readDWord(){
bool retVal = false;
if (MOTOR_POSITION == mnTypeOfIO){
int internalChecker;
TForteInt32 val;
internalChecker = readNumberFromFile(&val);
if (0 == internalChecker){
IN_D() = (TForteDWord) (val);
STATUS() = scmOK;
retVal = true;
}else if (1 == internalChecker){
STATUS() = scmNotInitialised;
DEVLOG_ERROR("Reading double word failed. The FB with PARAMS() = '%s' is not initialized.", PARAMS().getValue());
}else{
STATUS() = scmCouldNotRead;
}
}
if (!retVal){
DEVLOG_ERROR("Reading double word failed. The FB with PARAMS() = '%s' couldn't be read.", PARAMS().getValue());
}
return retVal;
}
bool CLMSEV3ProcessInterface::writeDWord(){
bool retVal = false;
if (mFile.is_open()){
bool writeAttempted = false;
mFile.clear();
mFile.seekp(0, std::ios::beg);
TForteDWord val = OUT_D();
switch (mnTypeOfIO){
case MOTOR_POSITION:{
TForteInt32 finalVal = (TForteInt32) val;
if (CIEC_INT::scm_nMinVal <= finalVal && CIEC_INT::scm_nMaxVal >= finalVal){
mFile << finalVal;
writeAttempted = true;
}else{
STATUS() = scmCouldNotWrite;
}
break;
}
default:{
STATUS() = scmCouldNotWrite;
break;
}
}
if (!mFile.fail() && writeAttempted) {
STATUS() = scmOK;
retVal = true;
}else{
STATUS() = scmCouldNotWrite;
}
}else{
STATUS() = scmNotInitialised;
}
if (!retVal){
DEVLOG_ERROR("Writing double word failed. The FB with PARAMS() = '%s' couldn't be written.", PARAMS().getValue());
}
return retVal;
}
/* INTIALIZATION FUNCTIONS **/
bool CLMSEV3ProcessInterface::setupLED(const std::vector<std::string> &paParamList, bool paIsInput){
bool retVal = false;
if (false == paIsInput){
std::string sysFileName("/sys/class/leds/ev3:" + paParamList[2] + ":" + paParamList[3] + ":ev3dev/brightness"); //format in ev3dev is= ev3:[left | right]:[red | green]:ev3dev
mFile.open(sysFileName.c_str(), std::fstream::out); //TODO change this when fully switching to C++11 for LMS EV3
if(mFile.is_open()){
if (true == writePin()){ // initialize output with default value (i.e, should be false)
mnTypeOfIO = LED;
retVal = true;
}
}else{
DEVLOG_ERROR("Opening file %s failed.", sysFileName.c_str());
}
}
return retVal;
}
bool CLMSEV3ProcessInterface::setupSensor(const std::vector<std::string> &paParamList, bool paIsInput){
bool retVal = false;
if (true == paIsInput){ //Output for sensor is not allowed
if (true == setupSensorValue(paParamList)){
mnTypeOfIO = SENSOR;
retVal = true;
}
}
return retVal;
}
bool CLMSEV3ProcessInterface::setupSensorW(const std::vector<std::string> &paParamList, bool paIsInput){
bool retVal = false;
if (scmModeID == paParamList[2]){ //mode of sensor can be input or output
if (true == setupSensorMode(paParamList, paIsInput)){
mnTypeOfIO = SENSORW_MODE;
retVal = true;
}
}else{ //value of sensor
if (true == paIsInput){
if (true == setupSensorValue(paParamList)){
mnTypeOfIO = SENSORW_VALUE;
retVal = true;
}
}
}
return retVal;
}
bool CLMSEV3ProcessInterface::setupSensorValue(const std::vector<std::string> &paParamList){
bool retVal = false;
std::string sysFileName;
std::string basePath = "/sys/class/lego-sensor/sensor";
std::stringstream helperStringStream;
int helperInteger = findNumberFromPort(basePath, paParamList[0]);
if(-1 != helperInteger){
helperStringStream << helperInteger;
if (!helperStringStream.fail()){
basePath += helperStringStream.str();
sysFileName = basePath + "/num_values";
mFile.open(sysFileName.c_str(), std::fstream::in); //TODO change this when fully switching to C++11 for LMS EV3
if(mFile.is_open()){
bool internalCheck;
TForteInt32 aux;
internalCheck = readNumberFromFile(&aux);
mFile.close();
if (0 == internalCheck){
if (0 <= aux){ //a negative value is not possible for num_values file
helperStringStream.str(paParamList[2]);
helperStringStream.clear();
helperStringStream.seekp(0, std::ios::beg);
helperStringStream >> helperInteger;
if (!helperStringStream.fail()){
if (helperInteger <= (aux - 1)){
sysFileName = basePath + "/value" + helperStringStream.str();
mFile.open(sysFileName.c_str(), std::fstream::in); //TODO change this when fully switching to C++11 for LMS EV3
if(mFile.is_open()){
retVal = true;
}else{
DEVLOG_ERROR("Opening file %s failed.", sysFileName.c_str());
}
}
}else{
DEVLOG_ERROR("Internal error trying to store a stringstream in an int.");
}
}
}else{
DEVLOG_ERROR("File %s was opened, but couln't be read.", sysFileName.c_str());
}
}else{
DEVLOG_ERROR("Opening file %s failed.", sysFileName.c_str());
}
}else{
DEVLOG_ERROR("Internal error trying to store an int in a stringstream");
}
}else{
DEVLOG_ERROR("Port %s wasn't found.", paParamList[0].c_str());
}
return retVal;
}
bool CLMSEV3ProcessInterface::setupSensorMode(const std::vector<std::string> &paParamList, bool paIsInput){
bool retVal = false;
std::string helperString;
std::string basePath = "/sys/class/lego-sensor/sensor";
std::stringstream helperStringStream;
int helperInteger = findNumberFromPort(basePath, paParamList[0]);
if(-1 != helperInteger){
helperStringStream << helperInteger;
if (!helperStringStream.fail()){
basePath += helperStringStream.str();
helperString = basePath + "/modes";
mFile.open(helperString.c_str(), std::fstream::in); //TODO change this when fully switching to C++11 for LMS EV3
if(mFile.is_open()){
while(std::getline(mFile, helperString, ' ')){
mModes.push_back(helperString);
}
mFile.close();
helperString = basePath + "/mode";
mFile.open(helperString.c_str(), paIsInput ? std::fstream::in : std::fstream::out); //TODO change this when fully switching to C++11 for LMS EV3
if(mFile.is_open()){
retVal = true;
}else{
DEVLOG_ERROR("Opening file %s failed.", helperString.c_str());
}
}else{
DEVLOG_ERROR("Opening file %s failed.", helperString.c_str());
}
}else{
DEVLOG_ERROR("Internal error trying to store an int in a stringstream");
}
}else{
DEVLOG_ERROR("Port %s wasn't found.", paParamList[0].c_str());
}
return retVal;
}
bool CLMSEV3ProcessInterface::setupButton(const std::string &paParam, bool paIsInput){
bool retVal = false;
if (true == paIsInput){
mstButtonVariables = new st_ButtonVariables;
mstButtonVariables->nmFd = open("/dev/input/event0", O_RDONLY);
if (-1 != mstButtonVariables->nmFd){
//Button constant found in linux/input.h
if (0 == paParam.compare("up")){
mstButtonVariables->nmbutton = 103;
}else if (0 == paParam.compare("down")){
mstButtonVariables->nmbutton = 108;
}else if (0 == paParam.compare("left")){
mstButtonVariables->nmbutton = 105;
}else if (0 == paParam.compare("right")){
mstButtonVariables->nmbutton = 106;
}else if (0 == paParam.compare("enter")){
mstButtonVariables->nmbutton = 28;
}else if (0 == paParam.compare("backspace")){
mstButtonVariables->nmbutton = 14;
}
if (0 != mstButtonVariables->nmbutton) {
mnTypeOfIO = BUTTON;
retVal = true;
}
}
if (!retVal){
delete mstButtonVariables;
}
}
return retVal;
}
bool CLMSEV3ProcessInterface::setupMotor(const std::vector<std::string>& paParamList, bool paIsInput){
bool retVal = false;
std::string sysFileName = "/sys/class/tacho-motor/motor";
std::stringstream number;
if (3 == paParamList.size()){
int sensorNumber = findNumberFromPort(sysFileName, paParamList[0]);
if(sensorNumber != -1){
number << sensorNumber;
if (!number.fail()){
bool defaultType = false;
if (paIsInput){
if (scmPWMID == paParamList[2]){
sysFileName += number.str() + "/duty_cycle";
mnTypeOfIO = MOTOR_PWM;
}else if (scmSPEEDID == paParamList[2]){
sysFileName += number.str() + "/speed";
mnTypeOfIO = MOTOR_SPEED;
}else if (scmRotID == paParamList[2]){
sysFileName += number.str() + "/count_per_rot";
mnTypeOfIO = MOTOR_ROT;
}else if (scmPositionID == paParamList[2]){
sysFileName += number.str() + "/position";
mnTypeOfIO = MOTOR_POSITION;
}else{
defaultType = true;
}
}else{
if (scmEnableID == paParamList[2]){
sysFileName += number.str() + "/command";
mnTypeOfIO = MOTOR_ENABLE;
}else if (scmResetID == paParamList[2]){
sysFileName += number.str() + "/command";
mnTypeOfIO = MOTOR_RESET;
}else if (scmPWMID == paParamList[2]){
sysFileName += number.str() + "/duty_cycle_sp";
mnTypeOfIO = MOTOR_PWM;
}else if (scmStopID == paParamList[2]){
sysFileName += number.str() + "/stop_command";
mnTypeOfIO = MOTOR_STOP;
}else if (scmPositionID == paParamList[2]){
sysFileName += number.str() + "/position";
mnTypeOfIO = MOTOR_POSITION;
}else{
defaultType = true;
}
}
if (!defaultType){
mFile.open(sysFileName.c_str(), paIsInput ? std::fstream::in : std::fstream::out); //TODO change this when fully switching to C++11 for LMS EV3
if(mFile.is_open()){
retVal = true;
}else{
mnTypeOfIO = UNDEFINED;
DEVLOG_ERROR("Opening file %s failed.", sysFileName.c_str());
}
}
}else{
DEVLOG_ERROR("Internal error trying to store an int in a stringstream");
}
}else{
DEVLOG_ERROR("Port %s wasn't found.", paParamList[0].c_str());
}
}
return retVal;
}
/* END OF INTIALIZATION FUNCTIONS **/
/* HELPER FUNCTIONS */
int CLMSEV3ProcessInterface::findNumberFromPort(const std::string &paBasePath, const std::string &paEv3Port){
int retVal = -1;
std::fstream mAddressFile;
std::string fullPath;
int portLenght = paEv3Port.length();
if (portLenght < 5){ //Only possible values are inX and outX according to input and output ports of the EV3
for(int i = 0; i < 255; i++){ //TODO: check maximum value of i
std::stringstream number;
number << i;
if (!number.fail()){
fullPath = paBasePath + number.str() + "/address";
mAddressFile.open(fullPath.c_str(), std::fstream::in); //TODO change this when fully switching to C++11 for LMS EV3ca
if(mAddressFile.is_open()){
char port[5];
mAddressFile.clear();
mAddressFile.seekg(0, std::ios::beg);
mAddressFile.read(port, portLenght);
if (!mAddressFile.fail()){
port[portLenght] = '\0';
if(paEv3Port.compare(port) == 0){
retVal = i;
break;
}
}
mAddressFile.close();
}
}
}
}
return retVal;
}
std::vector<std::string> CLMSEV3ProcessInterface::generateParameterList(){
std::stringstream streamBuf(std::string(PARAMS().getValue()));
std::string segment;
std::vector<std::string> retVal;
while(std::getline(streamBuf, segment, '.')){ //seperate the PARAMS input by '.' for easier processing
retVal.push_back(segment);
}
return retVal;
}
int CLMSEV3ProcessInterface::readNumberFromFile(TForteInt32* paResult){
int retVal = 0; //No error
if (mFile.is_open()){
std::string read;
mFile.clear();
mFile.seekg(0, std::ios::beg);
std::getline(mFile, read); //TODO: This return the same stream, should it be check? What behavior should it have if file is empty?
if (!mFile.fail()){
std::stringstream number(read);
number >> *paResult;
if (number.fail()){
retVal = 2; //Could not read
}
}else{
retVal = 2; //Could not read
}
}else{
retVal = 1; //Not initialized
}
return retVal;
}
/* END OF HELPER FUNCTIONS */