Merge "[567611] Code generator produces invalid code for FBs with 0 event ins or outs" into develop
diff --git a/src/arch/posix/CMakeLists.txt b/src/arch/posix/CMakeLists.txt
index 02f720a..720b06e 100644
--- a/src/arch/posix/CMakeLists.txt
+++ b/src/arch/posix/CMakeLists.txt
@@ -56,7 +56,7 @@
   mark_as_advanced(FORTE_RTTI_AND_EXCEPTIONS)
   
   if(FORTE_TESTS AND FORTE_USE_TEST_CONFIG_IN_FORTE)
-    forte_add_definition("-DBOOST_TEST_DYN_LINK -g -O0 --coverage -fno-inline -fno-elide-constructors -fno-exceptions -fsanitize=address -DBOOST_NO_EXCEPTIONS")
+    forte_add_definition("-DBOOST_TEST_DYN_LINK -g -O0 --coverage -fno-inline -fno-elide-constructors -fsanitize=address")
     forte_add_link_library("-fsanitize=address")
     forte_add_link_library("--coverage")
   else()
diff --git a/src/core/datatypes/forte_time.cpp b/src/core/datatypes/forte_time.cpp
index 8cac9c4..17ffb22 100644
--- a/src/core/datatypes/forte_time.cpp
+++ b/src/core/datatypes/forte_time.cpp
@@ -62,11 +62,9 @@
           case 'd':

             nTimeFactor = 24 * 60 * 60 * cgForteTimeBaseUnitsPerSecond;

             break;

-

           case 'h':

             nTimeFactor = 60 * 60 * cgForteTimeBaseUnitsPerSecond;

             break;

-

           case 'm':

             if('s' == tolower(*(pcEnd + 1))) {

               nTimeFactor = cgForteTimeBaseUnitsPerSecond / forte::core::constants::cMillisecondsPerSecond;

@@ -78,8 +76,9 @@
           case 'n':

             if('s' == tolower(*(pcEnd + 1))) {

               nTimeFactor = cgForteTimeBaseUnitsPerSecond / forte::core::constants::cNanosecondsPerSecond;

+              ++pcEnd;

             } else {

-              bEnd = true;

+              return -1;

             }

             break;

           case 's':

@@ -88,28 +87,32 @@
           case 'u':

             if('s' == tolower(*(pcEnd + 1))) {

               nTimeFactor = cgForteTimeBaseUnitsPerSecond / forte::core::constants::cMicrosecondsPerSecond;

+              ++pcEnd;

             } else {

-              bEnd = true;

+              return -1;

             }

             break;

           case '_':

             //ignore leading underscores

             break;

           default:

-            if(paValue == pcEnd) {

-              //we couldn't parse anything

+            if((pcEnd != paValue) || (0 == nIntVal)){   //we could not parse anything yet so wrong literal

+              //we have a number without unit or it is the first entry which we could not pars then this is an error

               return -1;

             }

+             // we are in an array and at the end of the literal

             bEnd = true;

             break;

         }

         nRetVal += static_cast<int>(pcEnd - paValue);

+        paValue = pcEnd;

         if(!bEnd) {

           ++nRetVal;

+          ++paValue;

         }

-        paValue = pcEnd + 1;

         nIntVal += (nBuf * nTimeFactor * nTimeSignFactor);

-      } while(('\0' != *paValue) && (!bEnd));

+      } while((!bEnd) && ('\0' != *paValue));

+

     } else {

       return -1;

     }

diff --git a/src/modules/PLC01A1/CMakeLists.txt b/src/modules/PLC01A1/CMakeLists.txt
new file mode 100644
index 0000000..9638b52
--- /dev/null
+++ b/src/modules/PLC01A1/CMakeLists.txt
@@ -0,0 +1,19 @@
+#*******************************************************************************
+# Copyright (c) 2019 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:
+#    Jose Cabral
+#    - initial API and implementation and/or initial documentation
+# *******************************************************************************/
+
+forte_add_io(PLC01A1 "Support for X-NUCLEO-PLC01A")
+
+if(FORTE_IO_PLC01A1)
+  forte_add_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+  forte_add_sourcefile_hcpp(plc01a1_controller plc01a1_config_fb)
+endif(FORTE_IO_PLC01A1)
diff --git a/src/modules/PLC01A1/plc01a1_config_fb.cpp b/src/modules/PLC01A1/plc01a1_config_fb.cpp
new file mode 100644
index 0000000..5ede19c
--- /dev/null
+++ b/src/modules/PLC01A1/plc01a1_config_fb.cpp
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ *   Jose Cabral - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "plc01a1_config_fb.h"
+#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
+#include "plc01a1_config_fb_gen.cpp"
+#endif
+
+#include "plc01a1_controller.h"
+
+DEFINE_FIRMWARE_FB(PLC01A1ConfigFB, g_nStringIdPLC01A1)
+
+
+
+const CStringDictionary::TStringId PLC01A1ConfigFB::scm_anDataInputNames[] = { g_nStringIdQI, g_nStringIdIN1, g_nStringIdIN2, g_nStringIdIN3, g_nStringIdIN4,
+  g_nStringIdIN5, g_nStringIdIN6, g_nStringIdIN7, g_nStringIdIN8, g_nStringIdOUT1, g_nStringIdOUT2, g_nStringIdOUT3, g_nStringIdOUT4, g_nStringIdOUT5,
+  g_nStringIdOUT6, g_nStringIdOUT7, g_nStringIdOUT8, g_nStringIdUpdateInterval };
+
+const CStringDictionary::TStringId PLC01A1ConfigFB::scm_anDataInputTypeIds[] = { g_nStringIdBOOL, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING,
+  g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING,
+  g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdWSTRING, g_nStringIdUINT };
+
+const CStringDictionary::TStringId PLC01A1ConfigFB::scm_anDataOutputNames[] = { g_nStringIdQO, g_nStringIdSTATUS };
+
+const CStringDictionary::TStringId PLC01A1ConfigFB::scm_anDataOutputTypeIds[] = { g_nStringIdBOOL, g_nStringIdWSTRING };
+
+const TForteInt16 PLC01A1ConfigFB::scm_anEIWithIndexes[] = { 0 };
+const TDataIOID PLC01A1ConfigFB::scm_anEIWith[] = { 0, 17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 255 };
+const CStringDictionary::TStringId PLC01A1ConfigFB::scm_anEventInputNames[] = { g_nStringIdINIT };
+const TDataIOID PLC01A1ConfigFB::scm_anEOWith[] = { 0, 1, 255, 0, 1, 255 };
+const TForteInt16 PLC01A1ConfigFB::scm_anEOWithIndexes[] = { 0, 3, -1 };
+const CStringDictionary::TStringId PLC01A1ConfigFB::scm_anEventOutputNames[] = { g_nStringIdINITO, g_nStringIdIND };
+const SFBInterfaceSpec PLC01A1ConfigFB::scm_stFBInterfaceSpec = {
+  1,  scm_anEventInputNames,  scm_anEIWith,  scm_anEIWithIndexes,
+  2,  scm_anEventOutputNames,  scm_anEOWith, scm_anEOWithIndexes,  18,  scm_anDataInputNames, scm_anDataInputTypeIds,
+  2,  scm_anDataOutputNames, scm_anDataOutputTypeIds,
+  0, 0
+};
+
+void PLC01A1ConfigFB::setInitialValues() {
+  UpdateInterval() = 25;
+}
+
+forte::core::io::IODeviceController* PLC01A1ConfigFB::createDeviceController(CDeviceExecution &paDeviceExecution) {
+  return new PLC01A1Controller(paDeviceExecution);
+}
+
+void PLC01A1ConfigFB::setConfig() {
+  PLC01A1Controller::Config config;
+  config.mUpdateInterval = UpdateInterval();
+  getDeviceController()->setConfig(&config);
+}
+
+void PLC01A1ConfigFB::onStartup() {
+  // Initialize handles
+  size_t initialDIOffset = 1;
+  size_t numberOfInputs = 8;
+  size_t numberOfOutputs = 8;
+
+  for(size_t i = 0; i < numberOfInputs; i++) {
+    PLC01A1Controller::HandleDescriptor desc = PLC01A1Controller::HandleDescriptor(*static_cast<CIEC_WSTRING*>(getDI(initialDIOffset + i)),
+      forte::core::io::IOMapper::In,
+      0 /*offset is always 0 */,
+      static_cast<uint8_t>(i));
+    initHandle(&desc);
+  }
+
+  for(size_t i = 0; i < numberOfOutputs; i++) {
+    PLC01A1Controller::HandleDescriptor
+    desc = PLC01A1Controller::HandleDescriptor(*static_cast<CIEC_WSTRING*>(getDI(initialDIOffset + numberOfInputs + i)),
+      forte::core::io::IOMapper::Out, 0 /*offset is always 0 */, static_cast<uint8_t>(numberOfOutputs - i - 1));
+    initHandle(&desc);
+  }
+
+  started();
+}
+
+
+
diff --git a/src/modules/PLC01A1/plc01a1_config_fb.h b/src/modules/PLC01A1/plc01a1_config_fb.h
new file mode 100644
index 0000000..ce6ebd2
--- /dev/null
+++ b/src/modules/PLC01A1/plc01a1_config_fb.h
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ *   Jose Cabral - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#ifndef _PLC01A1_H_
+#define _PLC01A1_H_
+
+#include <funcbloc.h>
+#include <forte_bool.h>
+#include <forte_uint.h>
+#include <forte_wstring.h>
+#include <io/configFB/io_configFB_controller.h>
+
+class PLC01A1ConfigFB : public forte::core::io::IOConfigFBController {
+  DECLARE_FIRMWARE_FB(PLC01A1ConfigFB)
+
+private:
+  static const CStringDictionary::TStringId scm_anDataInputNames[];
+  static const CStringDictionary::TStringId scm_anDataInputTypeIds[];
+  CIEC_BOOL &QI() {
+    return *static_cast<CIEC_BOOL*>(getDI(0));
+  };
+
+  CIEC_WSTRING &IN1() {
+    return *static_cast<CIEC_WSTRING*>(getDI(1));
+  };
+
+  CIEC_WSTRING &IN2() {
+    return *static_cast<CIEC_WSTRING*>(getDI(2));
+  };
+
+  CIEC_WSTRING &IN3() {
+    return *static_cast<CIEC_WSTRING*>(getDI(3));
+  };
+
+  CIEC_WSTRING &IN4() {
+    return *static_cast<CIEC_WSTRING*>(getDI(4));
+  };
+
+  CIEC_WSTRING &IN5() {
+    return *static_cast<CIEC_WSTRING*>(getDI(5));
+  };
+
+  CIEC_WSTRING &IN6() {
+    return *static_cast<CIEC_WSTRING*>(getDI(6));
+  };
+
+  CIEC_WSTRING &IN7() {
+    return *static_cast<CIEC_WSTRING*>(getDI(7));
+  };
+
+  CIEC_WSTRING &IN8() {
+    return *static_cast<CIEC_WSTRING*>(getDI(8));
+  };
+
+  CIEC_WSTRING &OUT1() {
+    return *static_cast<CIEC_WSTRING*>(getDI(9));
+  };
+
+  CIEC_WSTRING &OUT2() {
+    return *static_cast<CIEC_WSTRING*>(getDI(10));
+  };
+
+  CIEC_WSTRING &OUT3() {
+    return *static_cast<CIEC_WSTRING*>(getDI(11));
+  };
+
+  CIEC_WSTRING &OUT4() {
+    return *static_cast<CIEC_WSTRING*>(getDI(12));
+  };
+
+  CIEC_WSTRING &OUT5() {
+    return *static_cast<CIEC_WSTRING*>(getDI(13));
+  };
+
+  CIEC_WSTRING &OUT6() {
+    return *static_cast<CIEC_WSTRING*>(getDI(14));
+  };
+
+  CIEC_WSTRING &OUT7() {
+    return *static_cast<CIEC_WSTRING*>(getDI(15));
+  };
+
+  CIEC_WSTRING &OUT8() {
+    return *static_cast<CIEC_WSTRING*>(getDI(16));
+  };
+
+  CIEC_UINT &UpdateInterval() {
+    return *static_cast<CIEC_UINT*>(getDI(17));
+  };
+
+  static const CStringDictionary::TStringId scm_anDataOutputNames[];
+  static const CStringDictionary::TStringId scm_anDataOutputTypeIds[];
+  CIEC_BOOL &QO() {
+    return *static_cast<CIEC_BOOL*>(getDO(0));
+  };
+
+  CIEC_WSTRING &STATUS() {
+    return *static_cast<CIEC_WSTRING*>(getDO(1));
+  };
+
+  static const TEventID scm_nEventINITID = 0;
+  static const TForteInt16 scm_anEIWithIndexes[];
+  static const TDataIOID scm_anEIWith[];
+  static const CStringDictionary::TStringId scm_anEventInputNames[];
+
+  static const TEventID scm_nEventINITOID = 0;
+  static const TEventID scm_nEventINDID = 1;
+  static const TForteInt16 scm_anEOWithIndexes[];
+  static const TDataIOID scm_anEOWith[];
+  static const CStringDictionary::TStringId scm_anEventOutputNames[];
+
+  static const SFBInterfaceSpec scm_stFBInterfaceSpec;
+
+   FORTE_FB_DATA_ARRAY(2, 18, 2, 0);
+
+virtual void setInitialValues();
+
+  protected:
+    forte::core::io::IODeviceController* createDeviceController(CDeviceExecution &paDeviceExecution);
+  
+    void setConfig();
+
+    virtual void onStartup();
+
+  public:
+    FUNCTION_BLOCK_CTOR_WITH_BASE_CLASS(PLC01A1ConfigFB, forte::core::io::IOConfigFBController){}
+
+};
+
+#endif //close the ifdef sequence from the beginning of the file
+
diff --git a/src/modules/PLC01A1/plc01a1_controller.cpp b/src/modules/PLC01A1/plc01a1_controller.cpp
new file mode 100644
index 0000000..da61dbf
--- /dev/null
+++ b/src/modules/PLC01A1/plc01a1_controller.cpp
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ *   Jose Cabral - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include <io/mapper/io_handle_bit.h>
+#include "plc01a1_controller.h"
+#include <devlog.h>
+#include <sys/ioctl.h>
+#include <linux/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+const char *const PLC01A1Controller::scmSPIInputDevice = "/dev/spidev0.0";
+const char *const PLC01A1Controller::scmSPIOutputDevice = "/dev/spidev0.1";
+
+const char *const PLC01A1Controller::scmFailedToOpenInputControlFile = "Failed to open input control file";
+const char *const PLC01A1Controller::scmFailedToOpenOutputControlFile = "Failed to open output control file";
+
+const char *const PLC01A1Controller::scmFailedToSetInputMode = "Failed to set input mode";
+const char *const PLC01A1Controller::scmFailedToSetOutputMode = "Failed to set output mode";
+
+const char *const PLC01A1Controller::scmFailedToSetInputBits = "Failed to set input bits";
+const char *const PLC01A1Controller::scmFailedToSetOutputBits = "Failed to set output bits";
+
+const char *const PLC01A1Controller::scmFailedToSetInputSpeed = "Failed to set input speed";
+const char *const PLC01A1Controller::scmFailedToSetOutputSpeed = "Failed to set output speed";
+
+const uint32_t PLC01A1Controller::scmSPIMode = 0;
+
+const uint8_t PLC01A1Controller::scmSPIBits = 8;
+const uint32_t PLC01A1Controller::scmSPIInputMaxSpeed = 6250000; //6.25 MHz
+const uint32_t PLC01A1Controller::scmSPIOutputMaxSpeed = 5000000; //5 MHz
+
+
+
+PLC01A1Controller::PLC01A1Controller(CDeviceExecution &paDeviceExecution) :
+    forte::core::io::IODevicePollController(paDeviceExecution, 25), mSPIInputFd(0), mSPIOutputFd(0) {
+  memset(mInputArrayOld, 0, scmInputArrayLenght);
+  memset(mInputArray, 0, scmInputArrayLenght);
+  memset(mOutputArray, 0, scmOutputArrayLenght);
+  memset(mInputTX, 0, scmOutputArrayLenght);
+  memset(mOutputRX, 0, scmOutputArrayLenght);
+
+  memset(&mInputTR, 0, sizeof(struct spi_ioc_transfer));
+  memset(&mOutputTR, 0, sizeof(struct spi_ioc_transfer));
+
+  mInputTR.tx_buf = (unsigned long) mInputTX;
+  mInputTR.rx_buf = (unsigned long) mInputArray;
+  mInputTR.len = 2;
+  mInputTR.speed_hz = scmSPIInputMaxSpeed;
+  mInputTR.delay_usecs = 0;
+  mInputTR.bits_per_word = scmSPIBits;
+
+  mOutputTR.tx_buf = (unsigned long) mOutputArray;
+  mOutputTR.rx_buf = (unsigned long) mOutputRX;
+  mOutputTR.len = 2;
+  mOutputTR.speed_hz = scmSPIOutputMaxSpeed;
+  mOutputTR.delay_usecs = 0;
+  mOutputTR.bits_per_word = scmSPIBits;
+
+}
+
+void PLC01A1Controller::setConfig(struct forte::core::io::IODeviceController::Config *paConfig) {
+  Config newConfig = *static_cast<Config*>(paConfig);
+  setPollInterval(static_cast<float>(newConfig.mUpdateInterval));
+}
+
+const char* PLC01A1Controller::init() {
+
+  mSPIInputFd = open(scmSPIInputDevice, O_RDWR);
+  if(mSPIInputFd < 0) {
+    return scmFailedToOpenInputControlFile;
+  }
+
+  mSPIOutputFd = open(scmSPIOutputDevice, O_RDWR);
+  if(mSPIOutputFd < 0) {
+    return scmFailedToOpenOutputControlFile;
+  }
+
+  //SPI Mode
+  int ret = ioctl(mSPIInputFd, SPI_IOC_WR_MODE32, &scmSPIMode);
+  if(-1 == ret) {
+    return scmFailedToSetInputMode;
+  }
+
+  ret = ioctl(mSPIOutputFd, SPI_IOC_WR_MODE32, &scmSPIMode);
+  if(-1 == ret) {
+    return scmFailedToSetOutputMode;
+  }
+
+  //bits per word
+  ret = ioctl(mSPIInputFd, SPI_IOC_WR_BITS_PER_WORD, &scmSPIBits);
+  if(-1 == ret) {
+    return scmFailedToSetInputBits;
+  }
+
+  ret = ioctl(mSPIOutputFd, SPI_IOC_WR_BITS_PER_WORD, &scmSPIBits);
+  if(-1 == ret) {
+    return scmFailedToSetOutputBits;
+  }
+
+  // max speed hz
+  ret = ioctl(mSPIInputFd, SPI_IOC_WR_MAX_SPEED_HZ, &scmSPIInputMaxSpeed);
+    if(-1 == ret) {
+    return scmFailedToSetInputSpeed;
+  }
+
+  ret = ioctl(mSPIOutputFd, SPI_IOC_WR_MAX_SPEED_HZ, &scmSPIOutputMaxSpeed);
+  if(-1 == ret) {
+    return scmFailedToSetOutputSpeed;
+  }
+
+  DEVLOG_INFO("[PLC01A1Controller]: Initialization Correct!\n");
+
+  return 0;
+}
+
+void PLC01A1Controller::deInit() {
+  if(mSPIInputFd != 0) {
+    close(mSPIInputFd);
+    mSPIInputFd = 0;
+  }
+  if(mSPIOutputFd != 0) {
+    close(mSPIOutputFd);
+    mSPIOutputFd = 0;
+  }
+}
+
+void PLC01A1Controller::poll() {
+
+  int ret = ioctl(mSPIInputFd, SPI_IOC_MESSAGE(1), &mInputTR);
+  if(ret < 1) {
+    DEVLOG_ERROR("[PLC01A1Controller]: Failed sending SPI message to input controller");
+  }
+
+  // Check for updates and fire events
+  checkForInputChanges();
+
+  // Copy image to old image
+  memcpy(mInputArrayOld, mInputArray, scmInputArrayLenght);
+
+  output_parity_bits();
+
+  ret = ioctl(mSPIOutputFd, SPI_IOC_MESSAGE(1), &mOutputTR);
+  if(ret < 1) {
+    DEVLOG_ERROR("[PLC01A1Controller]: Failed sending SPI message to output controller");
+  }
+
+}
+
+bool PLC01A1Controller::isHandleValueEqual(forte::core::io::IOHandle *paHandle) {
+  return ((forte::core::io::IOHandleBit*) paHandle)->equal(mInputArrayOld);
+}
+
+forte::core::io::IOHandle* PLC01A1Controller::initHandle(forte::core::io::IODeviceController::HandleDescriptor *paHandleDescriptor) {
+  HandleDescriptor desc = *static_cast<HandleDescriptor*>(paHandleDescriptor);
+
+  return new forte::core::io::IOHandleBit(this, desc.mDirection, desc.mOffset, desc.mPosition,
+    desc.mDirection == forte::core::io::IOMapper::In ? mInputArray : mOutputArray);
+}
+
+void PLC01A1Controller::output_parity_bits() {
+
+  uint8_t outputBits[8] = { };
+  uint8_t parityBits[4] = { };
+
+  for(size_t i = 0; i < 8; i++) {
+    outputBits[i] = mOutputArray[0] & (0x80 >> i);
+    outputBits[i] = static_cast<uint8_t>(outputBits[i] >> (7 - i));
+  }
+
+  parityBits[3] = ((outputBits[7] ^ outputBits[5]) ^ outputBits[3]) ^ outputBits[1];
+  parityBits[3] = (parityBits[3] == 0x01) ? 0x08 : 0x00;
+
+
+  parityBits[2] = ((outputBits[6] ^ outputBits[4]) ^ outputBits[2]) ^ outputBits[0];
+  parityBits[2] = (parityBits[2] == 0x01) ? 0x04 : 0x00;
+
+  parityBits[1] = ((((((outputBits[7] ^ outputBits[6]) ^ outputBits[5]) ^ outputBits[4]) ^ outputBits[3])
+    ^ outputBits[2])
+    ^ outputBits[1]) ^ outputBits[0];
+  parityBits[1] = (parityBits[1] == 0x01) ? 0x02 : 0x00;
+
+
+  parityBits[0] = (parityBits[1] == 0x02) ? 0x00 : 0x01;
+
+  mOutputArray[1] = parityBits[3] | parityBits[2] | parityBits[1] | parityBits[0];
+}
+
diff --git a/src/modules/PLC01A1/plc01a1_controller.h b/src/modules/PLC01A1/plc01a1_controller.h
new file mode 100644
index 0000000..a76caca
--- /dev/null
+++ b/src/modules/PLC01A1/plc01a1_controller.h
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ *   Jose Cabral - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#ifndef SRC_MODULES_FESTO_CECC_FESTO_CONTROLLER_H_
+#define SRC_MODULES_FESTO_CECC_FESTO_CONTROLLER_H_
+
+#include <io/device/io_controller_poll.h>
+#include <linux/spi/spidev.h>
+
+class PLC01A1Controller : public forte::core::io::IODevicePollController {
+  public:
+    explicit PLC01A1Controller(CDeviceExecution &paDeviceExecution);
+
+    struct Config : forte::core::io::IODeviceController::Config {
+        unsigned int mUpdateInterval; //!< Sets the frequency for the data update cycle. The default value is 25 Hz.
+    };
+
+    class HandleDescriptor : public forte::core::io::IODeviceController::HandleDescriptor {
+      public:
+        uint8_t mOffset;
+        uint8_t mPosition;
+
+        HandleDescriptor(CIEC_WSTRING const &paId, forte::core::io::IOMapper::Direction paDirection, uint8_t paOffset, uint8_t paPosition) :
+            forte::core::io::IODeviceController::HandleDescriptor(paId, paDirection), mOffset(paOffset), mPosition(paPosition) {
+
+        }
+    };
+
+    void setConfig(struct forte::core::io::IODeviceController::Config* paConfig);
+
+    virtual bool isHandleValueEqual(forte::core::io::IOHandle* paHandle);
+
+    forte::core::io::IOHandle* initHandle(forte::core::io::IODeviceController::HandleDescriptor *paHandleDescriptor);
+
+  protected:
+    const char* init();
+    void deInit();
+
+    void poll();
+
+  private:
+
+    static const char *const scmSPIInputDevice;
+    static const char *const scmSPIOutputDevice;
+
+    int mSPIInputFd;
+    int mSPIOutputFd;
+
+    static const char * const scmFailedToOpenInputControlFile;
+    static const char *const scmFailedToOpenOutputControlFile;
+
+    static const uint32_t scmSPIMode;
+
+    static const char *const scmFailedToSetInputMode;
+    static const char *const scmFailedToSetOutputMode;
+
+    static const uint8_t scmSPIBits;
+
+    static const char *const scmFailedToSetInputBits;
+    static const char *const scmFailedToSetOutputBits;
+
+    static const uint32_t scmSPIInputMaxSpeed;
+    static const uint32_t scmSPIOutputMaxSpeed;
+
+    static const char *const scmFailedToSetInputSpeed;
+    static const char *const scmFailedToSetOutputSpeed;
+
+    static const size_t scmInputArrayLenght = 2;
+    static const size_t scmOutputArrayLenght = 2;
+
+    uint8_t mInputArrayOld[scmInputArrayLenght];
+    uint8_t mInputArray[scmInputArrayLenght];
+    uint8_t mOutputArray[scmOutputArrayLenght];
+
+    uint8_t mInputTX[scmInputArrayLenght];
+    uint8_t mOutputRX[scmInputArrayLenght];
+
+    struct spi_ioc_transfer mInputTR;
+    struct spi_ioc_transfer mOutputTR;
+
+    void output_parity_bits();
+};
+
+#endif /* SRC_MODULES_FESTO_CECC_FESTO_CONTROLLER_H_ */
diff --git a/tests/core/datatypes/CIEC_TIME_test.cpp b/tests/core/datatypes/CIEC_TIME_test.cpp
index ff5f1f8..905de44 100644
--- a/tests/core/datatypes/CIEC_TIME_test.cpp
+++ b/tests/core/datatypes/CIEC_TIME_test.cpp
@@ -341,6 +341,17 @@
   BOOST_CHECK_EQUAL(time.getInMicroSeconds(), -10325643);
 }
 
+BOOST_AUTO_TEST_CASE(parse_missing_s_in_microseconds_literal)
+{
+  CIEC_TIME time;
+
+  BOOST_CHECK_EQUAL(time.fromString("T#-1u"), -1);
+  BOOST_CHECK_EQUAL(time.getInMicroSeconds(), 0);
+
+  BOOST_CHECK_EQUAL(time.fromString("T#-1ut"), -1);
+  BOOST_CHECK_EQUAL(time.getInMicroSeconds(), 0);
+}
+
 BOOST_AUTO_TEST_CASE(parse_time_literal_in_nanoseconds)
 {
   CIEC_TIME time;
@@ -358,6 +369,14 @@
   BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 10325643);
 }
 
+BOOST_AUTO_TEST_CASE(parse_missing_s_in_nanoseconds_literal)
+{
+  CIEC_TIME time;
+
+  BOOST_CHECK_EQUAL(time.fromString("T#1234nx"), -1);
+  BOOST_CHECK_EQUAL(time.getInMicroSeconds(), 0);
+}
+
 BOOST_AUTO_TEST_CASE(parse_negative_signed_time_literal_in_nanoseconds)
 {
   CIEC_TIME time;
@@ -375,4 +394,56 @@
   BOOST_CHECK_EQUAL(time.getInNanoSeconds(), -10325643);
 }
 
+BOOST_AUTO_TEST_CASE(parse_time_literals_with_missing_end_unit)
+{
+  CIEC_TIME time;
+
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#68231"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#1234r"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#1h23"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#23m89"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#55s514"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#344ms1"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#344ms12399"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("T#344ms12399"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(parse_wrong_time_prefix)
+{
+  CIEC_TIME time;
+
+  BOOST_CHECK_EQUAL(-1, time.fromString("T23m"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("TIME23m"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("Tome#23m"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("Tine#23m"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+  BOOST_CHECK_EQUAL(-1, time.fromString("Tima#23m"));
+  BOOST_CHECK_EQUAL(time.getInNanoSeconds(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(parse_time_in_struct_or_array_literal)
+{
+  CIEC_TIME time;
+
+  BOOST_CHECK_EQUAL(7, time.fromString("T#999ms, "));
+  BOOST_CHECK_EQUAL(time.getInMilliSeconds(), 999);
+
+  BOOST_CHECK_EQUAL(6, time.fromString("T#23ms , "));
+  BOOST_CHECK_EQUAL(time.getInMilliSeconds(), 23);
+
+  BOOST_CHECK_EQUAL(12, time.fromString("T#10325643us,"));
+  BOOST_CHECK_EQUAL(time.getInMicroSeconds(), 10325643);
+}
+
 BOOST_AUTO_TEST_SUITE_END()