From 8a2d76dbb428c2047992976160f892ae21d3f72c Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Wed, 26 Feb 2025 14:32:35 -0500 Subject: [PATCH] adds_synergy_logic --- webserver/core/hardware_layers/synergy.cpp | 961 +++++++++++++++++++++ webserver/core/lib/iec_std_FB.h | 2 +- webserver/core/lib/synergy.h | 751 ++++++++++++++++ webserver/lib/standard_FB.txt | 1 + webserver/lib/synergy.txt | 290 +++++++ webserver/scripts/change_hardware_layer.sh | 7 + webserver/webserver.py | 2 + 7 files changed, 2013 insertions(+), 1 deletion(-) create mode 100644 webserver/core/hardware_layers/synergy.cpp create mode 100644 webserver/core/lib/synergy.h create mode 100644 webserver/lib/synergy.txt diff --git a/webserver/core/hardware_layers/synergy.cpp b/webserver/core/hardware_layers/synergy.cpp new file mode 100644 index 0000000..b255fa8 --- /dev/null +++ b/webserver/core/hardware_layers/synergy.cpp @@ -0,0 +1,961 @@ +/* +MIT License + +Copyright (c) 2024 FACTS Engineering, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include // File control options +#include // Manipulates underlying device parameters of special files +#include +#include + +#include +#include +#include // Standard symbolic constants and types +#include + +#include "ladder.h" +#include "custom_layer.h" + +#define NUMBER_OF_MODULES 15 +#define slaveSelectPin 27 +#define slaveAckPin 25 +#define baseEnable 17 + +#define SPI_MODE SPI_MODE2 + +#define MOD_HDR 0x02 +#define VERSION_HDR 0x03 +#define ACTIVE_HDR 0x04 +#define DROPOUT_HDR 0x05 +#define CFG_HDR 0x10 +#define READ_CFG_HDR 0x11 +#define PETWD_HDR 0x30 +#define STARTWD_HDR 0x31 +#define STOPWD_HDR 0x32 +#define CONFIGWD_HDR 0x33 +#define READ_STATUS_HDR 0x40 +#define READ_DISCRETE_HDR 0x50 +#define READ_ANALOG_HDR 0x51 +#define READ_BLOCK_HDR 0x52 +#define WRITE_DISCRETE_HDR 0x60 +#define WRITE_ANALOG_HDR 0x61 +#define WRITE_BLOCK_HDR 0x62 +#define FW_UPDATE_HDR 0xAA + +#define DUMMY 0xFF +#define EMPTY_SLOT_ID 0xFFFFFFFE +#define MAX_TIMEOUT 0xFFFFFFFF + +#define HIGH 1 +#define LOW 0 + +#define SPI_DEVPATH "/dev/spidev6.0" +#define GPIO_DEVPATH "/dev/gpiochip0" + +#define DEBUG_PRINT_ON +#define GEN_DEBUG 1 +#define LOG_ERROR log_error(__LINE__) + +#define OK 0 +#define ERROR -1 + +using namespace std; + +/************************************************************************ + * MODULES * +************************************************************************/ +const struct moduleProps +{ + unsigned int moduleID; + char diBytes; // Number of bytes used by all Discrete Input channels + char doBytes; // Number of bytes used by all Discrete Output channels + char aiBytes; // Number of bytes used by all Analog Input channels + char aoBytes; // Number of bytes used by all Analog Output channels + char statusBytes; // Number of status bytes. Things like overrange errors or missing 24V. + char configBytes; // Number of bytes used to configure module. Things like ranges and enabled channels. + char dataSize; // Resolution or Specialty info + const char* moduleName; // Text name of module +}mdb[] = { + // {0x000000ID,di,do,ai,ao,st,cf,ds} + {0x00000000, 0, 0, 0, 0, 0, 0, 0, "Empty"}, //Empty first entry for defaults + {0x04A010E1, 1, 0, 0, 0, 0, 0, 1, "SLM-DCI-8NP"}, + {0x04A000E5, 1, 0, 0, 0, 0, 0, 1, "SLM-ACI-8"}, + {0x04A000E7, 1, 0, 0, 0, 0, 0, 1, "SLM-SIM-8"}, + {0x04A000E8, 1, 0, 0, 0, 0, 0, 1, "SLM-ACDCI-8NP"}, + {0x052000E2, 2, 0, 0, 0, 0, 0, 1, "SLM-DCI-16NP"}, + {0x052000E9, 2, 0, 0, 0, 0, 0, 1, "SLM-ACDCI-16NP"}, + {0x140300E0, 0, 1, 0, 0, 0, 0, 1, "SLM-RLY-4-ISO"}, + {0x1403F4E1, 0, 0, 0, 32, 4, 4, 0xA0, "SLM-PWM-4"}, + {0x140400ED, 0, 1, 0, 0, 0, 0, 1, "SLM-ACO-8"}, + {0x140400EF, 0, 1, 0, 0, 0, 0, 1, "SLM-RLY-8-ISO"}, + {0x140400F1, 0, 2, 0, 0, 0, 0, 1, "SLM-RLY-16"}, + {0x04A000E2, 1, 0, 0, 0, 0, 0, 1, "SLM-TTL-8ND"}, + {0x140500E1, 0, 1, 0, 0, 0, 0, 1, "SLM-DCO-8N"}, + {0x140500E2, 0, 1, 0, 0, 0, 0, 1, "SLM-DCO-8P"}, + {0x140500E6, 0, 1, 0, 0, 0, 0, 1, "SLM-TTL-08TD"}, + {0x140800E5, 0, 2, 0, 0, 0, 0, 1, "SLM-DCO-15N"}, + {0x140800E6, 0, 2, 0, 0, 0, 0, 1, "SLM-DCO-15P"}, + {0x24A500E1, 1, 1, 0, 0, 0, 0, 1, "SLM-ACDCI-8NP-RLY8"}, + {0x24A500E2, 1, 1, 0, 0, 0, 0, 1, "SLM-DCI-8NP-DCO-7N"}, + {0x24A500E3, 1, 1, 0, 0, 0, 0, 1, "SLM-DCI-8NP-DCO-7P"}, + {0x346055E1, 0, 0, 16, 0, 12, 18, 16, "SLM-04AD-Bipolar"}, + {0x346055E8, 0, 0, 16, 0, 12, 8, 16, "SLM-RTD-4"}, + {0x346055E2, 0, 0, 16, 0, 12, 2, 16, "SLM-AI-4AD-mA"}, + {0x346055E3, 0, 0, 16, 0, 12, 2, 16, "SLM-AI-4AD-V"}, + {0x346055EF, 0, 0, 16, 0, 12, 2, 12, "SLM-AI-4ADL-mA"}, + {0x346055E0, 0, 0, 16, 0, 12, 2, 12, "SLM-AI-4ADL-V"}, + {0x34608CE1, 0, 0, 16, 0, 12, 20, 32, "SLM-THM-4"}, + {0x34608CEE, 0, 0, 16, 0, 12, 8, 32, "SLM-NTC-4"}, + {0x34A055EA, 0, 0, 32, 0, 12, 2, 12, "SLM-AI-8-mA"}, + {0x34A055EB, 0, 0, 32, 0, 12, 2, 12, "SLM-AI-8-V"}, + {0x440355E3, 0, 0, 0, 16, 4, 0, 12, "SLM-AO-4DAL-mA"}, + {0x440355E4, 0, 0, 0, 16, 4, 0, 12, "SLM-AO-4DAL-V"}, + {0x440555E8, 0, 0, 0, 32, 4, 0, 12, "SLM-AO-8-mA"}, + {0x440555E9, 0, 0, 0, 32, 4, 0, 12, "SLM-AO-8-V"}, + {0x5461A7E3, 0, 0, 16, 8, 12, 0, 12, "SLM-AI4-AO2-mA"}, + {0x5461A7E4, 0, 0, 16, 8, 12, 0, 12, "SLM-AI4-AO2-V"}, + {0xFFFFFFFF, 0, 0, 0, 0, 0, 0, 0, "BAD SLOT"}, //empty in case no modules are defined. + {0x00000000, 0, 0, 0, 0, 0, 0, 0, "BAD SLOT"} //empty in case no modules are defined. +}; + +struct moduleInfo{ + uint8_t dbLoc; // MDB location +}baseSlot[NUMBER_OF_MODULES]; + +const char SLM_AIH_4_mA_DEFAULT_CONFIG[] = {0x40,0x03}; +const char SLM_AIH_4_V_DEFAULT_CONFIG[] = {0x40,0x03}; +const char SLM_AI_4_mA_DEFAULT_CONFIG[] = {0x40,0x03}; +const char SLM_AI_4_V_DEFAULT_CONFIG[] = {0x40,0x03}; +const char SLM_AI_8_mA_DEFAULT_CONFIG[] = {0x40,0x07}; +const char SLM_AI_8_V_DEFAULT_CONFIG[] = {0x40,0x07}; +const char SLM_PWM_4_DEFAULT_CONFIG[] = {0x02,0x02,0x02,0x02}; +// const char SLM_AI4_AO2_mA_DEFAULT_CONFIG[] = {0x40,0x03}; +// const char SLM_AI4_AO2_V_DEFAULT_CONFIG[] = {0x40,0x03}; +const char SLM_NTC_4_DEFAULT_CONFIG[] = {0x40,0x03,0x60,0x05, + 0x20,0x00,0x80,0x02}; +const char SLM_THM_4_DEFAULT_CONFIG[] = {0x40,0x03,0x60,0x05, + 0x21,0x00,0x22,0x00, + 0x23,0x00,0x24,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00}; +const char SLM_RTD_4_DEFAULT_CONFIG[] = {0x40,0x03,0x60,0x05, + 0x20,0x01,0x80,0x00}; +const char SLM_AIH_4_VmA_DEFAULT_CONFIG[] = {0x40,0x03,0x00,0x00, + 0x20,0x03,0x00,0x00, + 0x21,0x03,0x00,0x00, + 0x22,0x03,0x00,0x00, + 0x23,0x03}; +const char SLM_HSC_DEFAULT_CONFIG[] = {0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01}; + + +int log_error(int line) +{ + char log_msg[1000]; + +#ifdef GEN_DEBUG + sprintf(log_msg, "!line %d\n", line); + log(log_msg); +#endif + return ERROR; +} + +/************************************************************************ + * RASPBERRY Pi Functions * +************************************************************************/ +int digitalWrite(int offset, uint8_t value){ + struct gpiohandle_request rq; + struct gpiohandle_data data; + int chip_fd, line_fd; + + // Open GPIO Device + chip_fd = open(GPIO_DEVPATH, O_RDONLY); + if (chip_fd < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to open GPIO device." << endl; + #endif + return -1; + } + + // Set GPIO line as output + rq.lineoffsets[0] = offset; + rq.flags = GPIOHANDLE_REQUEST_OUTPUT; + rq.lines = 1; + + if(ioctl(chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &rq) < 0 ){ + #ifdef DEBUG_PRINT_ON + cerr << "Failed to get line handle." << endl; + #endif + close(chip_fd); + return -1; + } + + line_fd = rq.fd; + + // Set GPIO state + data.values[0] = value; + if (ioctl(line_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to set GPIO value." << endl; + #endif + close(line_fd); + close(chip_fd); + return -1; + } + close(line_fd); + close(chip_fd); + return 0; +} + +int digitalRead(int offset){ + struct gpiohandle_request rq; + struct gpiohandle_data data; + int chip_fd, line_fd, reading; + + // Open GPIO Device + chip_fd = open(GPIO_DEVPATH, O_RDONLY); + if (chip_fd < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to open GPIO device." << endl; + #endif + return -1; + } + + // Set GPIO line as input + rq.lineoffsets[0] = offset; + rq.flags = GPIOHANDLE_REQUEST_INPUT; + rq.lines = 1; + + if(ioctl(chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &rq) < 0 ){ + #ifdef DEBUG_PRINT_ON + cerr << "Failed to get line handle." << endl; + #endif + close(chip_fd); + return -1; + } + + line_fd = rq.fd; + + // Read GPIO state + if (ioctl(line_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to set GPIO value." << endl; + #endif + close(line_fd); + close(chip_fd); + return -1; + } + + reading = +data.values[0]; + close(line_fd); + close(chip_fd); + return reading; +} + +uint8_t rpiSPITransfer(int fd, uint8_t txByte){ + uint8_t rxByte; + + struct spi_ioc_transfer spi; + memset(&spi, 0, sizeof(spi)); + + spi.tx_buf = (unsigned long)&txByte; + spi.rx_buf = (unsigned long)&rxByte; + spi.len = 1; + spi.delay_usecs = 0; + spi.speed_hz = 1000000; + + if (ioctl(fd, SPI_IOC_MESSAGE(1), &spi) < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "SPI transfer failed." << endl; + #endif + return -1; + } + return rxByte; +} + +int rpiSPIBegin(int mode){ + int spispeed = 1000000; + + + int fd = open(SPI_DEVPATH, O_RDONLY); + if (fd < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to open SPI device." << endl; + #endif + } + + if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to set SPI mode." << endl; + #endif + return -1; + } + + if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &spispeed) < 0) { + #ifdef DEBUG_PRINT_ON + cerr << "Failed to set SPI speed." << endl; + #endif + return -1; + } + return fd; +} + +void rpiSPIEnd(int fd){ + close(fd); +} + +void delay(int value){ + usleep(1000 * value); +} + +void delayMicroseconds(int value){ + usleep(value); +} + +uint32_t millis(){ + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return static_cast(ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL); +} + +/************************************************************************ + * SPI Functions * +************************************************************************/ +uint8_t spiSendRecvByte(uint8_t data){ + + int fd = rpiSPIBegin(SPI_MODE_2); + + digitalWrite(slaveSelectPin, LOW); + data = rpiSPITransfer(fd, data); + digitalWrite(slaveSelectPin, HIGH); + + rpiSPIEnd(fd); + + return data; +} + +uint32_t spiSendRecvInt(uint32_t data){ // will be used to read analog data + uint8_t rData[4]; + uint8_t tData[4]; + uint32_t returnInt = 0; + + tData[0] = (data>>0) & 0xFF; // Data to write to the Base Controller - 1 byte long + tData[1] = (data>>8) & 0xFF; // Data to write to the Base Controller - 1 byte long + tData[2] = (data>>16) & 0xFF; // Data to write to the Base Controller - 1 byte long + tData[3] = (data>>24) & 0xFF; // Data to write to the Base Controller - 1 byte long + + int fd = rpiSPIBegin(SPI_MODE_2); + digitalWrite(slaveSelectPin, LOW); + + rData[0] = rpiSPITransfer(fd, tData[0]); + rData[1] = rpiSPITransfer(fd, tData[1]); + rData[2] = rpiSPITransfer(fd, tData[2]); + rData[3] = rpiSPITransfer(fd, tData[3]); + digitalWrite(slaveSelectPin, HIGH); + rpiSPIEnd(fd); + + returnInt = (rData[3]<<24); + returnInt += (rData[2]<<16); + returnInt += (rData[1]<<8); + returnInt += (rData[0]<<0); + + return returnInt; +} + +void spiSendRecvBuf(uint8_t *buf, int len, bool returnData = 0){ + + int fd = rpiSPIBegin(SPI_MODE_2); + digitalWrite(slaveSelectPin, LOW); + + if(returnData){ + for(int i = 0; i < len; ++i){ + buf[i] = rpiSPITransfer(fd, buf[i]); // Return what we get into our buffer + } + } + else{ + for(int i = 0; i < len; ++i){ + rpiSPITransfer(fd, buf[i]); // Or don't return what we get + } + } + + digitalWrite(slaveSelectPin, HIGH); + rpiSPIEnd(fd); + return; +} + +bool spiTimeout(uint32_t uS,uint8_t resendMsg = 0,uint16_t retryPeriod = 0){ + uint16_t retryTime = 0; + + while((!digitalRead(slaveAckPin)) && (uS != 0)){ + delayMicroseconds(1); + uS--; + + retryTime++; + if((retryPeriod) && (retryTime == retryPeriod)){ // If we specified a retry period and it equals the time we've waited + + spiSendRecvByte(resendMsg); + retryTime = 0; + } + } + delayMicroseconds(50); // Small delay to let Base Controller load next msg in buf + + if(uS > 0){ + return 1; + } + else{ + #ifdef DEBUG_PRINT_ON + // debugPrintln("Timeout"); + #endif + return 0; + } +} + +void dataSync(){ + uint32_t currentMillis = 0; + uint32_t startMillis = 0; + + startMillis = millis(); + while(!digitalRead(slaveAckPin)){ + currentMillis = millis(); + if(currentMillis - startMillis >= 200){ + #ifdef DEBUG_PRINT_ON + // debugPrintln("Base Sync Timeout"); + #endif + break; + } + } + delayMicroseconds(1); + + startMillis = millis(); + while(digitalRead(slaveAckPin)){ + currentMillis = millis(); + if(currentMillis - startMillis >= 200){ + #ifdef DEBUG_PRINT_ON + // debugPrintln("Base Sync Timeout"); + #endif + break; + } + } + delayMicroseconds(1); + + startMillis = millis(); + while(!digitalRead(slaveAckPin)){ + currentMillis = millis(); + if(currentMillis - startMillis >= 200){ + #ifdef DEBUG_PRINT_ON + // debugPrintln("Base Sync Timeout"); + #endif + break; + } + } + delayMicroseconds(1); + + return; +} + +/************************************************************************ +* DISCRETE MODULE FUNCTIONS * +************************************************************************/ +uint32_t readDiscrete(uint8_t slot, uint8_t channel = 0){ + uint32_t data = 0; + uint8_t len = 0; + uint8_t mdbLoc = 0; + char rData[4] = {0,0,0,0}; + + mdbLoc = baseSlot[slot-1].dbLoc; + len = mdb[mdbLoc].diBytes; + + if((slot < 1) || (slot > NUMBER_OF_MODULES)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slots must be between 1 and "); + // debugPrintln(NUMBER_OF_MODULES); + #endif + return 0; + } + + if((len <= 0)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slot "); + // debugPrint(slot); + // debugPrintln(": This module has no Discrete Input bytes"); + #endif + return 0; + } + + if(channel > (len * 8)){ //8 channels per byte + #ifdef DEBUG_PRINT_ON + // debugPrintln("This channel is not valid"); + #endif + return 0; + } + + rData[0] = READ_DISCRETE_HDR; + rData[1] = slot; + spiSendRecvBuf((uint8_t *)rData,2); + memset(rData,0,4); //clear buffer + if(spiTimeout(1000*200) == true){ + spiSendRecvBuf((uint8_t *)rData,len,true); + data = (rData[3]<<24); + data += (rData[2]<<16); + data += (rData[1]<<8); + data += (rData[0]<<0); + + if(channel != 0){ + data = (data>>(channel-1)) & 1; // shift and mask + } + dataSync(); + return data; + } + else{ + #ifdef DEBUG_PRINT_ON + // debugPrintln("Slow read"); + #endif + delay(100); + return 0; + } +} + +void writeDiscrete(uint32_t data,uint8_t slot, uint8_t channel = 0){ + uint8_t tData[7]; + uint8_t mdbLoc = 0; + uint8_t len = 0; + + mdbLoc = baseSlot[slot-1].dbLoc; + len = mdb[mdbLoc].doBytes; + + if((slot < 1) || (slot > NUMBER_OF_MODULES)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slots must be between 1 and "); + // debugPrintln(NUMBER_OF_MODULES); + #endif + return; + } + + if((len <= 0)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slot "); + // debugPrint(slot); + // debugPrintln(": This module has no Discrete Output bytes"); + #endif + return; + } + + if(channel > (len * 8)){ //8 channels per byte + #ifdef DEBUG_PRINT_ON + // debugPrintln("This channel is not valid"); + #endif + return; + } + + tData[0] = WRITE_DISCRETE_HDR; + tData[1] = slot; + tData[2] = channel; + + if(channel == 0){ + for(int i=0;i>(8*i) & 0xFF; //Shift and mask + } + } + else{ + tData[3] = data & 0b1; //mask bit + len = 1; //only send 1 byte + } + + + spiSendRecvBuf(tData,len+3); //3 Header bytes plus data length + + dataSync(); + return; +} + +/************************************************************************ +* ANALOG MODULE FUNCTIONS * +************************************************************************/ +int readAnalog(uint8_t slot, uint8_t channel){ + int data = 0; + uint8_t len = 0; + uint8_t mdbLoc = 0; + char rData[4] = {0,0,0,0}; + + mdbLoc = baseSlot[slot-1].dbLoc; + len = mdb[mdbLoc].aiBytes; + + if((slot < 1) || (slot > NUMBER_OF_MODULES)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slots must be between 1 and "); + // debugPrintln(NUMBER_OF_MODULES); + #endif + return 0; + } + + if((len <= 0)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slot "); + // debugPrint(slot); + // debugPrintln(": This module has no Analog Input bytes"); + #endif + return 0; + } + + if((channel <= 0) || (channel > (len / 4))){ //4 bytes per channel + #ifdef DEBUG_PRINT_ON + // debugPrintln("This channel is not valid"); + #endif + return 0; + } + + rData[0] = READ_ANALOG_HDR; + rData[1] = slot; + rData[2] = channel; + spiSendRecvBuf((uint8_t *)rData,3); + + if(spiTimeout(1000*200) == true){ + data = spiSendRecvInt(DUMMY); + dataSync(); + return data; + } + else{ + #ifdef DEBUG_PRINT_ON + // debugPrintln("Slow read"); + #endif + delay(100); + return 0; + } +} + +float readTemperature(uint8_t slot, uint8_t channel){ + char log_msg[1000]; + union int2float{ + int data; //We get an int back no matter what analog module. Unions set variables as the same/ + float temperature; //Floats and Ints are both 32 bits, so this lets quickly convert our int to a float. + }ourValue; + + ourValue.data = readAnalog(slot,channel); //Use the same analog read function to get int + #ifdef DEBUG_PRINT_ON + // sprintf(log_msg, "Slot %d: Channel %d: %.2f\n", slot, channel, ourValue.temperature); + // log(log_msg); + #endif + + return ourValue.temperature; //return the float +} + +void writeAnalog(uint32_t data,uint8_t slot, uint8_t channel){ + uint8_t tData[7]; + uint8_t mdbLoc = 0; + uint8_t len = 0; + + mdbLoc = baseSlot[slot-1].dbLoc; + len = mdb[mdbLoc].aoBytes; + + if((slot < 1) || (slot > NUMBER_OF_MODULES)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slots must be between 1 and "); + // debugPrintln(NUMBER_OF_MODULES); + #endif + return; + } + + if((len <= 0)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slot "); + // debugPrint(slot); + // debugPrintln(": This module has no Analog Output bytes"); + #endif + return; + } + + if((channel <= 0) || (channel > (len / 4))){ //4 bytes per channel + #ifdef DEBUG_PRINT_ON + // debugPrintln("This channel is not valid"); + #endif + return; + } + + tData[0] = WRITE_ANALOG_HDR; + tData[1] = slot; + tData[2] = channel; + tData[3] = (data>>0) & 0xFF; + tData[4] = (data>>8) & 0xFF; + tData[5] = (data>>16) & 0xFF; + tData[6] = (data>>24) & 0xFF; + spiSendRecvBuf(tData,7); + + dataSync(); + return; +} + +/************************************************************************ + * BASE CONTROLLER FUNCTIONS * +************************************************************************/ + +bool handleHDR(uint8_t HDR){ + + while(!digitalRead(slaveAckPin)); // Wait for Base Controller to be out of base scanning + spiSendRecvByte(HDR); // Send intital Header to ping DMA + + return spiTimeout(MAX_TIMEOUT,HDR,2000); // 1 if we got Base Controller ack, 0 if we took too long +} + +bool configureModule(char cfgData[], uint8_t slot){ + uint8_t len = 0; + uint8_t mdbLoc = 0; + uint8_t cfgForSpi[66]; + + mdbLoc = baseSlot[slot-1].dbLoc; + len = mdb[mdbLoc].configBytes + 2; + cfgForSpi[0] = CFG_HDR; + cfgForSpi[1] = slot; + + memcpy(cfgForSpi+2,cfgData,len); + delay(1); + if((slot < 1) || (slot > NUMBER_OF_MODULES)){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slots must be between 1 and "); + // debugPrintln(NUMBER_OF_MODULES); + #endif + return 0; + } + + if(len <= 0){ + #ifdef DEBUG_PRINT_ON + // debugPrint("Slot "); + // debugPrint(slot); + // debugPrintln(": This module has no config Bytes"); + #endif + return 0; + } + + spiSendRecvBuf(cfgForSpi,len); + delay(100); // Additional time for Config to written + dataSync(); + dataSync(); + return 1; +} + +/******************************************************************************* +Overload of above function for const arrays +*******************************************************************************/ +bool configureModule(const char cfgData[], uint8_t slot){ + + return configureModule((char*)cfgData, slot); + +} + +char *loadConfigBuf(int moduleID){ + + switch(moduleID){ + case 0x34608CE1: + return (char*)SLM_THM_4_DEFAULT_CONFIG; + case 0x34A055EA: + return (char*)SLM_AI_8_mA_DEFAULT_CONFIG; + case 0x34A055EB: + return (char*)SLM_AI_8_V_DEFAULT_CONFIG; + case 0x1403F4E1: + return (char*)SLM_PWM_4_DEFAULT_CONFIG; + default: + break; + } + return (char*)SLM_AI_4_mA_DEFAULT_CONFIG; +} + +void enableBaseController(bool state){ + + digitalWrite(baseEnable, state); + +} + +uint8_t printModules(){ + char log_msg[1000]; + uint32_t slot = 0; + uint8_t dbLoc = 0; + uint8_t goodSlots = 0; + + while(baseSlot[slot].dbLoc != 0 && slot < NUMBER_OF_MODULES){ + dbLoc = baseSlot[slot].dbLoc; + if(mdb[dbLoc].moduleID == 0 || mdb[dbLoc].moduleID == 0xFFFFFFFF){ + #ifdef DEBUG_PRINT_ON + sprintf(log_msg, "slot %d: Sign On Error\n", slot + 1); + log(log_msg); + #endif + } + else{ + #ifdef DEBUG_PRINT_ON + sprintf(log_msg, "slot %d: %s\n", slot + 1, mdb[dbLoc].moduleName); + log(log_msg); + #endif + goodSlots++; + } + slot++; + } + dataSync(); + return goodSlots; +} + +/************************************************************************ + * BASE INITIALIZATION FUNCTION * +************************************************************************/ +uint8_t initHardware() { + char log_msg[1000]; + uint32_t slots = 0; + int dbLoc = 0; + char *cfgArray; + const uint8_t max_slots = 15; + + union moduleIDs{ // Use a union as a quick convert between byte arrays and ints + uint32_t IDs[max_slots]; + uint8_t byteArray[max_slots * 4]; + }modules; + uint8_t retry = 0; + + digitalWrite(slaveSelectPin, HIGH); + memset(baseSlot,0,sizeof(baseSlot)); // Clear base constants array + enableBaseController(HIGH); // Start base controller + delay(100); + + if(spiTimeout(1000*5000) == false){ + #ifdef DEBUG_PRINT_ON + sprintf(log_msg, "No Base Controller Activity.\nCheck External Supply Connection.\n"); + log(log_msg); + #endif + delay(1000); + return 0; + } + + while(((slots == 0) || (slots > 15)) && (retry < 5)){ // Begin sign-on until number of slots are valid. Only retry 5 times. + if(handleHDR(MOD_HDR)){ + delay(5); + slots = spiSendRecvByte(DUMMY); // Get number of modules in base + if(slots == 0 || slots > 15){ + if(retry > 2){ + enableBaseController(LOW); // Disable base controller + delay(10); + enableBaseController(HIGH); // Start base controller + delay(10); + } + retry++; // Let Base Controller retry + } + } + } + + if(retry >= 5){ // Zero module in the base. Quit sign-on routine and let the user know + #ifdef DEBUG_PRINT_ON + sprintf(log_msg, "Zero modules in the base.\n"); + log(log_msg); + #endif + delay(500); + return 0; + } + + if(slots > NUMBER_OF_MODULES){ + #ifdef DEBUG_PRINT_ON + sprintf(log_msg, "\nToo many modules in Base\nDevice only supports %d\n", NUMBER_OF_MODULES); + log(log_msg); + #endif + memset(baseSlot, 0, sizeof(baseSlot)); // Clear base constants array + return 0; + } + + spiTimeout(1000*200); + spiSendRecvBuf(modules.byteArray,slots*4,1); // Slots * 4 bytes per ID code + + uint8_t baseControllerConstants[1 * max_slots * 7]; // Seven elements in module sign on + for(uint32_t i=0;i 0){ // Modules with config Bytes need to havea config loaded + cfgArray = loadConfigBuf(mdb[dbLoc].moduleID); // Get pointer to default config for this module + while(!configureModule(cfgArray, i + 1)){ // configure module + #ifdef DEBUG_PRINT_ON + // debugPrintln("Working"); + #endif + } + } + } + #endif + + delay(50); // Let the Base Controller complete its end of the sign-on + #ifdef DEBUG_PRINT_ON + printModules(); // Returns the number of good modules. Prints IDs to serial monitor + #endif + return slots; +} + +/************************************************************************ + * Open PLC * +************************************************************************/ +//----------------------------------------------------------------------------- +// Functions called by the main OpenPLC routine +//----------------------------------------------------------------------------- +void initializeHardware() +{ + // char log_msg[1000]; + // sprintf(log_msg, "Synergy: INITIALIZE HARDWARE\n"); + // log(log_msg); +} + +void finalizeHardware() +{ +} + +void updateBuffersIn() +{ + pthread_mutex_lock(&bufferLock); //lock mutex + pthread_mutex_unlock(&bufferLock); //unlock mutex +} + +void updateBuffersOut() +{ + pthread_mutex_lock(&bufferLock); //lock mutex + pthread_mutex_unlock(&bufferLock); //unlock mutex +} \ No newline at end of file diff --git a/webserver/core/lib/iec_std_FB.h b/webserver/core/lib/iec_std_FB.h index dfb01b7..857b910 100755 --- a/webserver/core/lib/iec_std_FB.h +++ b/webserver/core/lib/iec_std_FB.h @@ -1892,7 +1892,7 @@ __end: #if defined(SL_RP4) #include "SL-RP4.h" #endif - +#include "synergy.h" diff --git a/webserver/core/lib/synergy.h b/webserver/core/lib/synergy.h new file mode 100644 index 0000000..c0b1ca2 --- /dev/null +++ b/webserver/core/lib/synergy.h @@ -0,0 +1,751 @@ +/************************************************************************ + * DECLARATION OF Synergy LIB BLOCKS * +************************************************************************/ +// SLM_INIT +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(BOOL,INIT) + __DECLARE_VAR(BOOL,SUCCESS) + + // FB private variables - TEMP, private and located variables + +} SLM_INIT; + +// SLM_DISCRETE_OUT_8 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(BOOL,O1) + __DECLARE_VAR(BOOL,O2) + __DECLARE_VAR(BOOL,O3) + __DECLARE_VAR(BOOL,O4) + __DECLARE_VAR(BOOL,O5) + __DECLARE_VAR(BOOL,O6) + __DECLARE_VAR(BOOL,O7) + __DECLARE_VAR(BOOL,O8) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_DISCRETE_OUT_8; + +// SLM_DISCRETE_OUT_15 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(BOOL,O1) + __DECLARE_VAR(BOOL,O2) + __DECLARE_VAR(BOOL,O3) + __DECLARE_VAR(BOOL,O4) + __DECLARE_VAR(BOOL,O5) + __DECLARE_VAR(BOOL,O6) + __DECLARE_VAR(BOOL,O7) + __DECLARE_VAR(BOOL,O8) + __DECLARE_VAR(BOOL,O9) + __DECLARE_VAR(BOOL,O10) + __DECLARE_VAR(BOOL,O11) + __DECLARE_VAR(BOOL,O12) + __DECLARE_VAR(BOOL,O13) + __DECLARE_VAR(BOOL,O14) + __DECLARE_VAR(BOOL,O15) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_DISCRETE_OUT_15; + +// SLM_DISCRETE_OUT_16 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(BOOL,O1) + __DECLARE_VAR(BOOL,O2) + __DECLARE_VAR(BOOL,O3) + __DECLARE_VAR(BOOL,O4) + __DECLARE_VAR(BOOL,O5) + __DECLARE_VAR(BOOL,O6) + __DECLARE_VAR(BOOL,O7) + __DECLARE_VAR(BOOL,O8) + __DECLARE_VAR(BOOL,O9) + __DECLARE_VAR(BOOL,O10) + __DECLARE_VAR(BOOL,O11) + __DECLARE_VAR(BOOL,O12) + __DECLARE_VAR(BOOL,O13) + __DECLARE_VAR(BOOL,O14) + __DECLARE_VAR(BOOL,O15) + __DECLARE_VAR(BOOL,O16) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_DISCRETE_OUT_16; + +// SLM_DISCRETE_IN_8 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(BOOL,I1) + __DECLARE_VAR(BOOL,I2) + __DECLARE_VAR(BOOL,I3) + __DECLARE_VAR(BOOL,I4) + __DECLARE_VAR(BOOL,I5) + __DECLARE_VAR(BOOL,I6) + __DECLARE_VAR(BOOL,I7) + __DECLARE_VAR(BOOL,I8) + + // FB private variables - TEMP, private and located variables + +} SLM_DISCRETE_IN_8; + +// SLM_DISCRETE_IN_16 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(BOOL,I1) + __DECLARE_VAR(BOOL,I2) + __DECLARE_VAR(BOOL,I3) + __DECLARE_VAR(BOOL,I4) + __DECLARE_VAR(BOOL,I5) + __DECLARE_VAR(BOOL,I6) + __DECLARE_VAR(BOOL,I7) + __DECLARE_VAR(BOOL,I8) + __DECLARE_VAR(BOOL,I9) + __DECLARE_VAR(BOOL,I10) + __DECLARE_VAR(BOOL,I11) + __DECLARE_VAR(BOOL,I12) + __DECLARE_VAR(BOOL,I13) + __DECLARE_VAR(BOOL,I14) + __DECLARE_VAR(BOOL,I15) + __DECLARE_VAR(BOOL,I16) + + // FB private variables - TEMP, private and located variables + +} SLM_DISCRETE_IN_16; + +// SLM_ANALOG_IN_4 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(UINT,I1) + __DECLARE_VAR(UINT,I2) + __DECLARE_VAR(UINT,I3) + __DECLARE_VAR(UINT,I4) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_ANALOG_IN_4; + +// SLM_ANALOG_IN_8 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(UINT,I1) + __DECLARE_VAR(UINT,I2) + __DECLARE_VAR(UINT,I3) + __DECLARE_VAR(UINT,I4) + __DECLARE_VAR(UINT,I5) + __DECLARE_VAR(UINT,I6) + __DECLARE_VAR(UINT,I7) + __DECLARE_VAR(UINT,I8) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_ANALOG_IN_8; + +// SLM_ANALOG_OUT_4 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(UINT,O1) + __DECLARE_VAR(UINT,O2) + __DECLARE_VAR(UINT,O3) + __DECLARE_VAR(UINT,O4) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_ANALOG_OUT_4; + +// SLM_ANALOG_OUT_8 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(UINT,O1) + __DECLARE_VAR(UINT,O2) + __DECLARE_VAR(UINT,O3) + __DECLARE_VAR(UINT,O4) + __DECLARE_VAR(UINT,O5) + __DECLARE_VAR(UINT,O6) + __DECLARE_VAR(UINT,O7) + __DECLARE_VAR(UINT,O8) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_ANALOG_OUT_8; + +// SLM_TEMPERATURE_4 +// Data part +typedef struct { + // FB Interface - IN, OUT, IN_OUT variables + __DECLARE_VAR(BOOL,EN) + __DECLARE_VAR(BOOL,ENO) + __DECLARE_VAR(SINT,SLOT) + __DECLARE_VAR(REAL,I1) + __DECLARE_VAR(REAL,I2) + __DECLARE_VAR(REAL,I3) + __DECLARE_VAR(REAL,I4) + + // FB private variables - TEMP, private and located variables + __DECLARE_VAR(SINT,DUMMY) + +} SLM_TEMPERATURE_4; +/************************************************************************ + * END OF Synergy LIB BLOCKS * +************************************************************************/ + +/************************************************************************ + * DECLARATION LIBRARY FUNCTION CALLS * +************************************************************************/ +uint8_t initHardware(); +void writeDiscrete(uint32_t data,uint8_t slot, uint8_t channel = 0); +uint32_t readDiscrete(uint8_t slot, uint8_t channel = 0); +int readAnalog(uint8_t slot, uint8_t channel); +void writeAnalog(uint32_t data,uint8_t slot, uint8_t channel); +float readTemperature(uint8_t slot, uint8_t channel); + +/************************************************************************ + * DECLARATION OF Synergy LIB BLOCKS * +************************************************************************/ +static void SLM_INIT_init__(SLM_INIT *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->INIT,0,retain) + __INIT_VAR(data__->SUCCESS,0,retain) +} + +// Code part +static void SLM_INIT_body__(SLM_INIT *data__) { + static uint8_t init = 0; + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + if(init == 0) { + initHardware(); + init = 1; + } + // __SET_VAR(data__->,SUCCESS,,initHardware()); + __SET_VAR(data__->,SUCCESS,,__BOOL_LITERAL(TRUE)); + + goto __end; + +__end: + return; +} // SLM_INIT_body__() + +/************************************************************************ + * DISCRETE LIB BLOCKS * +************************************************************************/ +static void SLM_DISCRETE_OUT_8_init__(SLM_DISCRETE_OUT_8 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->O1,0,retain) + __INIT_VAR(data__->O2,0,retain) + __INIT_VAR(data__->O3,0,retain) + __INIT_VAR(data__->O4,0,retain) + __INIT_VAR(data__->O5,0,retain) + __INIT_VAR(data__->O6,0,retain) + __INIT_VAR(data__->O7,0,retain) + __INIT_VAR(data__->O8,0,retain) +} + +// Code part +static void SLM_DISCRETE_OUT_8_body__(SLM_DISCRETE_OUT_8 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + uint8_t output_byte = __GET_VAR(data__->O8) << 7 | + __GET_VAR(data__->O7) << 6 | + __GET_VAR(data__->O6) << 5 | + __GET_VAR(data__->O5) << 4 | + __GET_VAR(data__->O4) << 3 | + __GET_VAR(data__->O3) << 2 | + __GET_VAR(data__->O2) << 1 | + __GET_VAR(data__->O1); + writeDiscrete(output_byte, __GET_VAR(data__->SLOT), 0); + + goto __end; + +__end: + return; +} // SLM_DISCRETE_OUT_8_body__() + +static void SLM_DISCRETE_OUT_15_init__(SLM_DISCRETE_OUT_15 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->O1,0,retain) + __INIT_VAR(data__->O2,0,retain) + __INIT_VAR(data__->O3,0,retain) + __INIT_VAR(data__->O4,0,retain) + __INIT_VAR(data__->O5,0,retain) + __INIT_VAR(data__->O6,0,retain) + __INIT_VAR(data__->O7,0,retain) + __INIT_VAR(data__->O8,0,retain) + __INIT_VAR(data__->O9,0,retain) + __INIT_VAR(data__->O10,0,retain) + __INIT_VAR(data__->O11,0,retain) + __INIT_VAR(data__->O12,0,retain) + __INIT_VAR(data__->O13,0,retain) + __INIT_VAR(data__->O14,0,retain) + __INIT_VAR(data__->O15,0,retain) +} + +// Code part +static void SLM_DISCRETE_OUT_15_body__(SLM_DISCRETE_OUT_15 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + uint16_t output_byte = __GET_VAR(data__->O15) << 14 | + __GET_VAR(data__->O14) << 13 | + __GET_VAR(data__->O13) << 12 | + __GET_VAR(data__->O12) << 11 | + __GET_VAR(data__->O11) << 10 | + __GET_VAR(data__->O10) << 9 | + __GET_VAR(data__->O9) << 8 | + __GET_VAR(data__->O8) << 7 | + __GET_VAR(data__->O7) << 6 | + __GET_VAR(data__->O6) << 5 | + __GET_VAR(data__->O5) << 4 | + __GET_VAR(data__->O4) << 3 | + __GET_VAR(data__->O3) << 2 | + __GET_VAR(data__->O2) << 1 | + __GET_VAR(data__->O1); + writeDiscrete(output_byte, __GET_VAR(data__->SLOT), 0); + + goto __end; + +__end: + return; +} // SLM_DISCRETE_OUT_15_body__() + +static void SLM_DISCRETE_OUT_16_init__(SLM_DISCRETE_OUT_16 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->O1,0,retain) + __INIT_VAR(data__->O2,0,retain) + __INIT_VAR(data__->O3,0,retain) + __INIT_VAR(data__->O4,0,retain) + __INIT_VAR(data__->O5,0,retain) + __INIT_VAR(data__->O6,0,retain) + __INIT_VAR(data__->O7,0,retain) + __INIT_VAR(data__->O8,0,retain) + __INIT_VAR(data__->O9,0,retain) + __INIT_VAR(data__->O10,0,retain) + __INIT_VAR(data__->O11,0,retain) + __INIT_VAR(data__->O12,0,retain) + __INIT_VAR(data__->O13,0,retain) + __INIT_VAR(data__->O14,0,retain) + __INIT_VAR(data__->O15,0,retain) + __INIT_VAR(data__->O16,0,retain) +} + +// Code part +static void SLM_DISCRETE_OUT_16_body__(SLM_DISCRETE_OUT_16 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + uint16_t output_byte = __GET_VAR(data__->O16) << 15 | + __GET_VAR(data__->O15) << 14 | + __GET_VAR(data__->O14) << 13 | + __GET_VAR(data__->O13) << 12 | + __GET_VAR(data__->O12) << 11 | + __GET_VAR(data__->O11) << 10 | + __GET_VAR(data__->O10) << 9 | + __GET_VAR(data__->O9) << 8 | + __GET_VAR(data__->O8) << 7 | + __GET_VAR(data__->O7) << 6 | + __GET_VAR(data__->O6) << 5 | + __GET_VAR(data__->O5) << 4 | + __GET_VAR(data__->O4) << 3 | + __GET_VAR(data__->O3) << 2 | + __GET_VAR(data__->O2) << 1 | + __GET_VAR(data__->O1); + writeDiscrete(output_byte, __GET_VAR(data__->SLOT), 0); + + goto __end; + +__end: + return; +} // SLM_DISCRETE_OUT_16_body__() + +static void SLM_DISCRETE_IN_8_init__(SLM_DISCRETE_IN_8 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->I1,0,retain) + __INIT_VAR(data__->I2,0,retain) + __INIT_VAR(data__->I3,0,retain) + __INIT_VAR(data__->I4,0,retain) + __INIT_VAR(data__->I5,0,retain) + __INIT_VAR(data__->I6,0,retain) + __INIT_VAR(data__->I7,0,retain) + __INIT_VAR(data__->I8,0,retain) +} + +// Code part +static void SLM_DISCRETE_IN_8_body__(SLM_DISCRETE_IN_8 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + #define bitRead(value, bit) (((value) >> (bit)) & 0x01) + uint32_t input_byte = readDiscrete(__GET_VAR(data__->SLOT), 0); + __SET_VAR(data__->,I1,,bitRead(input_byte, 0)); + __SET_VAR(data__->,I2,,bitRead(input_byte, 1)); + __SET_VAR(data__->,I3,,bitRead(input_byte, 2)); + __SET_VAR(data__->,I4,,bitRead(input_byte, 3)); + __SET_VAR(data__->,I5,,bitRead(input_byte, 4)); + __SET_VAR(data__->,I6,,bitRead(input_byte, 5)); + __SET_VAR(data__->,I7,,bitRead(input_byte, 6)); + __SET_VAR(data__->,I8,,bitRead(input_byte, 7)); + + goto __end; + +__end: + return; +} // SLM_DISCRETE_IN_8_body__() + +static void SLM_DISCRETE_IN_16_init__(SLM_DISCRETE_IN_16 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->I1,0,retain) + __INIT_VAR(data__->I2,0,retain) + __INIT_VAR(data__->I3,0,retain) + __INIT_VAR(data__->I4,0,retain) + __INIT_VAR(data__->I5,0,retain) + __INIT_VAR(data__->I6,0,retain) + __INIT_VAR(data__->I7,0,retain) + __INIT_VAR(data__->I8,0,retain) + __INIT_VAR(data__->I9,0,retain) + __INIT_VAR(data__->I10,0,retain) + __INIT_VAR(data__->I11,0,retain) + __INIT_VAR(data__->I12,0,retain) + __INIT_VAR(data__->I13,0,retain) + __INIT_VAR(data__->I14,0,retain) + __INIT_VAR(data__->I15,0,retain) + __INIT_VAR(data__->I16,0,retain) +} + +// Code part +static void SLM_DISCRETE_IN_16_body__(SLM_DISCRETE_IN_16 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + #define bitRead(value, bit) (((value) >> (bit)) & 0x01) + uint32_t input_byte = readDiscrete(__GET_VAR(data__->SLOT), 0); + __SET_VAR(data__->,I1,,bitRead(input_byte, 0)); + __SET_VAR(data__->,I2,,bitRead(input_byte, 1)); + __SET_VAR(data__->,I3,,bitRead(input_byte, 2)); + __SET_VAR(data__->,I4,,bitRead(input_byte, 3)); + __SET_VAR(data__->,I5,,bitRead(input_byte, 4)); + __SET_VAR(data__->,I6,,bitRead(input_byte, 5)); + __SET_VAR(data__->,I7,,bitRead(input_byte, 6)); + __SET_VAR(data__->,I8,,bitRead(input_byte, 7)); + __SET_VAR(data__->,I9,,bitRead(input_byte, 8)); + __SET_VAR(data__->,I10,,bitRead(input_byte, 9)); + __SET_VAR(data__->,I11,,bitRead(input_byte, 10)); + __SET_VAR(data__->,I12,,bitRead(input_byte, 11)); + __SET_VAR(data__->,I13,,bitRead(input_byte, 12)); + __SET_VAR(data__->,I14,,bitRead(input_byte, 13)); + __SET_VAR(data__->,I15,,bitRead(input_byte, 14)); + __SET_VAR(data__->,I16,,bitRead(input_byte, 15)); + + goto __end; + +__end: + return; +} // SLM_DISCRETE_IN_16_body__() + +/************************************************************************ + * ANALOG LIB BLOCKS * +************************************************************************/ + +static void SLM_ANALOG_IN_4_init__(SLM_ANALOG_IN_4 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->I1,0,retain) + __INIT_VAR(data__->I2,0,retain) + __INIT_VAR(data__->I3,0,retain) + __INIT_VAR(data__->I4,0,retain) +} + +// Code part +static void SLM_ANALOG_IN_4_body__(SLM_ANALOG_IN_4 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + char msg[100]; + uint16_t input_byte = readAnalog(__GET_VAR(data__->SLOT), 1); + __SET_VAR(data__->,I1,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 2); + __SET_VAR(data__->,I2,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 3); + __SET_VAR(data__->,I3,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 4); + __SET_VAR(data__->,I4,,input_byte); + + goto __end; + +__end: + return; +} // SLM_ANALOG_IN_4_body__() + +static void SLM_ANALOG_IN_8_init__(SLM_ANALOG_IN_8 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->I1,0,retain) + __INIT_VAR(data__->I2,0,retain) + __INIT_VAR(data__->I3,0,retain) + __INIT_VAR(data__->I4,0,retain) + __INIT_VAR(data__->I5,0,retain) + __INIT_VAR(data__->I6,0,retain) + __INIT_VAR(data__->I7,0,retain) + __INIT_VAR(data__->I8,0,retain) +} + +// Code part +static void SLM_ANALOG_IN_8_body__(SLM_ANALOG_IN_8 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + char msg[100]; + uint16_t input_byte = readAnalog(__GET_VAR(data__->SLOT), 1); + __SET_VAR(data__->,I1,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 2); + __SET_VAR(data__->,I2,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 3); + __SET_VAR(data__->,I3,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 4); + __SET_VAR(data__->,I4,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 5); + __SET_VAR(data__->,I5,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 6); + __SET_VAR(data__->,I6,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 7); + __SET_VAR(data__->,I7,,input_byte); + input_byte = readAnalog(__GET_VAR(data__->SLOT), 8); + __SET_VAR(data__->,I8,,input_byte); + + goto __end; + +__end: + return; +} // SLM_ANALOG_IN_8_body__() + +static void SLM_ANALOG_OUT_4_init__(SLM_ANALOG_OUT_4 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->O1,0,retain) + __INIT_VAR(data__->O2,0,retain) + __INIT_VAR(data__->O3,0,retain) + __INIT_VAR(data__->O4,0,retain) +} + +// Code part +static void SLM_ANALOG_OUT_4_body__(SLM_ANALOG_OUT_4 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + char msg[100]; + uint32_t data = __GET_VAR(data__->O1); + writeAnalog(data, __GET_VAR(data__->SLOT), 1); + data = __GET_VAR(data__->O2); + writeAnalog(data, __GET_VAR(data__->SLOT), 2); + data = __GET_VAR(data__->O3); + writeAnalog(data, __GET_VAR(data__->SLOT), 3); + data = __GET_VAR(data__->O4); + writeAnalog(data, __GET_VAR(data__->SLOT), 4); + + goto __end; + +__end: + return; +} // SLM_ANALOG_OUT_4_body__() + +static void SLM_ANALOG_OUT_8_init__(SLM_ANALOG_OUT_8 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->O1,0,retain) + __INIT_VAR(data__->O2,0,retain) + __INIT_VAR(data__->O3,0,retain) + __INIT_VAR(data__->O4,0,retain) + __INIT_VAR(data__->O5,0,retain) + __INIT_VAR(data__->O6,0,retain) + __INIT_VAR(data__->O7,0,retain) + __INIT_VAR(data__->O8,0,retain) +} + +// Code part +static void SLM_ANALOG_OUT_8_body__(SLM_ANALOG_OUT_8 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + char msg[100]; + uint32_t data = __GET_VAR(data__->O1); + writeAnalog(data, __GET_VAR(data__->SLOT), 1); + data = __GET_VAR(data__->O2); + writeAnalog(data, __GET_VAR(data__->SLOT), 2); + data = __GET_VAR(data__->O3); + writeAnalog(data, __GET_VAR(data__->SLOT), 3); + data = __GET_VAR(data__->O4); + writeAnalog(data, __GET_VAR(data__->SLOT), 4); + data = __GET_VAR(data__->O5); + writeAnalog(data, __GET_VAR(data__->SLOT), 5); + data = __GET_VAR(data__->O6); + writeAnalog(data, __GET_VAR(data__->SLOT), 6); + data = __GET_VAR(data__->O7); + writeAnalog(data, __GET_VAR(data__->SLOT), 7); + data = __GET_VAR(data__->O8); + writeAnalog(data, __GET_VAR(data__->SLOT), 8); + + goto __end; + +__end: + return; +} // SLM_ANALOG_OUT_8_body__() + +static void SLM_TEMPERATURE_4_init__(SLM_TEMPERATURE_4 *data__, BOOL retain) { + __INIT_VAR(data__->EN,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->ENO,__BOOL_LITERAL(TRUE),retain) + __INIT_VAR(data__->SLOT,0,retain) + __INIT_VAR(data__->DUMMY,0,retain) + __INIT_VAR(data__->I1,0,retain) + __INIT_VAR(data__->I2,0,retain) + __INIT_VAR(data__->I3,0,retain) + __INIT_VAR(data__->I4,0,retain) +} + +// Code part +static void SLM_TEMPERATURE_4_body__(SLM_TEMPERATURE_4 *data__) { + // Control execution + if (!__GET_VAR(data__->EN)) { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(FALSE)); + return; + } + else { + __SET_VAR(data__->,ENO,,__BOOL_LITERAL(TRUE)); + } + char msg[100]; + float input_byte = readTemperature(__GET_VAR(data__->SLOT), 1); + __SET_VAR(data__->,I1,,input_byte); + input_byte = readTemperature(__GET_VAR(data__->SLOT), 2); + __SET_VAR(data__->,I2,,input_byte); + input_byte = readTemperature(__GET_VAR(data__->SLOT), 3); + __SET_VAR(data__->,I3,,input_byte); + input_byte = readTemperature(__GET_VAR(data__->SLOT), 4); + __SET_VAR(data__->,I4,,input_byte); + + goto __end; + +__end: + return; +} // SLM_TEMPERATURE_4_body__() +/************************************************************************ + * END OF Synergy LIB BLOCKS * +************************************************************************/ diff --git a/webserver/lib/standard_FB.txt b/webserver/lib/standard_FB.txt index 6009bb1..25b8cc3 100755 --- a/webserver/lib/standard_FB.txt +++ b/webserver/lib/standard_FB.txt @@ -45,6 +45,7 @@ {#include "sema.txt" } {#include "communication_blocks.txt" } {#include "sm_cards.txt" } +{#include "synergy.txt" } {#include "SL-RP4.txt" } {enable code generation} diff --git a/webserver/lib/synergy.txt b/webserver/lib/synergy.txt new file mode 100644 index 0000000..d4c2f1e --- /dev/null +++ b/webserver/lib/synergy.txt @@ -0,0 +1,290 @@ +(* + * This file is part of OpenPLC - an open source Programmable + * Logic Controller compliant with IEC 61131-3 + * + * Copyright (C) 2021 Thiago Alves + * + * See COPYING and COPYING.LESSER files for copyright details. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + + + +(* + * An IEC 61131-3 compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + + +(* + * This is a dummy implementation of those block since + * its code is actually written in C, not ST + *) + +(* + * SLM_INIT + * ------------- + *) +FUNCTION_BLOCK SLM_INIT + VAR_INPUT + INIT : BOOL; + END_VAR + VAR_OUTPUT + SUCCESS : BOOL; + END_VAR + SUCCESS := 0; +END_FUNCTION_BLOCK + +(* + * SLM_DISCRETE_OUT_8 + * ------------- + *) +FUNCTION_BLOCK SLM_DISCRETE_OUT_8 + VAR_INPUT + SLOT : SINT; + O1 : BOOL; + O2 : BOOL; + O3 : BOOL; + O4 : BOOL; + O5 : BOOL; + O6 : BOOL; + O7 : BOOL; + O8 : BOOL; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := SLOT; +END_FUNCTION_BLOCK + +(* + * SLM_DISCRETE_OUT_15 + * ------------- + *) +FUNCTION_BLOCK SLM_DISCRETE_OUT_15 + VAR_INPUT + SLOT : SINT; + O1 : BOOL; + O2 : BOOL; + O3 : BOOL; + O4 : BOOL; + O5 : BOOL; + O6 : BOOL; + O7 : BOOL; + O8 : BOOL; + O9 : BOOL; + O10 : BOOL; + O11 : BOOL; + O12 : BOOL; + O13 : BOOL; + O14 : BOOL; + O15 : BOOL; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := SLOT; +END_FUNCTION_BLOCK + +(* + * SLM_DISCRETE_OUT_16 + * ------------- + *) +FUNCTION_BLOCK SLM_DISCRETE_OUT_16 + VAR_INPUT + SLOT : SINT; + O1 : BOOL; + O2 : BOOL; + O3 : BOOL; + O4 : BOOL; + O5 : BOOL; + O6 : BOOL; + O7 : BOOL; + O8 : BOOL; + O9 : BOOL; + O10 : BOOL; + O11 : BOOL; + O12 : BOOL; + O13 : BOOL; + O14 : BOOL; + O15 : BOOL; + O16 : BOOL; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := SLOT; +END_FUNCTION_BLOCK + +(* + * SL_DISCRETE_IN_8 + * ------------- + *) +FUNCTION_BLOCK SLM_DISCRETE_IN_8 + VAR_INPUT + SLOT : SINT; + END_VAR + VAR_OUTPUT + I1 : BOOL; + I2 : BOOL; + I3 : BOOL; + I4 : BOOL; + I5 : BOOL; + I6 : BOOL; + I7 : BOOL; + I8 : BOOL; + END_VAR + I1 := 0; +END_FUNCTION_BLOCK + +(* + * SLM_DISCRETE_IN_16 + * ------------- + *) +FUNCTION_BLOCK SLM_DISCRETE_IN_16 + VAR_INPUT + SLOT : SINT; + END_VAR + VAR_OUTPUT + I1 : BOOL; + I2 : BOOL; + I3 : BOOL; + I4 : BOOL; + I5 : BOOL; + I6 : BOOL; + I7 : BOOL; + I8 : BOOL; + I9 : BOOL; + I10 : BOOL; + I11 : BOOL; + I12 : BOOL; + I13 : BOOL; + I14 : BOOL; + I15 : BOOL; + I16 : BOOL; + END_VAR + I1 := 0; +END_FUNCTION_BLOCK + +(* + * SLM_ANALOG_IN_4 + * ------------- + *) +FUNCTION_BLOCK SLM_ANALOG_IN_4 + VAR_INPUT + SLOT : SINT; + END_VAR + VAR_OUTPUT + I1 : UINT; + I2 : UINT; + I3 : UINT; + I4 : UINT; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := 0; +END_FUNCTION_BLOCK + +(* + * SLM_ANALOG_IN_8 + * ------------- + *) +FUNCTION_BLOCK SLM_ANALOG_IN_8 + VAR_INPUT + SLOT : SINT; + END_VAR + VAR_OUTPUT + I1 : UINT; + I2 : UINT; + I3 : UINT; + I4 : UINT; + I5 : UINT; + I6 : UINT; + I7 : UINT; + I8 : UINT; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := 0; +END_FUNCTION_BLOCK + +(* + * SLM_ANALOG_OUT_4 + * ------------- + *) +FUNCTION_BLOCK SLM_ANALOG_OUT_4 + VAR_INPUT + SLOT : SINT; + O1 : UINT; + O2 : UINT; + O3 : UINT; + O4 : UINT; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := 0; +END_FUNCTION_BLOCK + +(* + * SLM_ANALOG_OUT_8 + * ------------- + *) +FUNCTION_BLOCK SLM_ANALOG_OUT_8 + VAR_INPUT + SLOT : SINT; + O1 : UINT; + O2 : UINT; + O3 : UINT; + O4 : UINT; + O5 : UINT; + O6 : UINT; + O7 : UINT; + O8 : UINT; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := 0; +END_FUNCTION_BLOCK + +(* + * SLM_TEMPERATURE_4 + * ------------- + *) +FUNCTION_BLOCK SLM_TEMPERATURE_4 + VAR_INPUT + SLOT : SINT; + END_VAR + VAR_OUTPUT + I1 : REAL; + I2 : REAL; + I3 : REAL; + I4 : REAL; + END_VAR + VAR + DUMMY : SINT; + END_VAR + DUMMY := 0; +END_FUNCTION_BLOCK \ No newline at end of file diff --git a/webserver/scripts/change_hardware_layer.sh b/webserver/scripts/change_hardware_layer.sh index 12f7a5d..15dc426 100755 --- a/webserver/scripts/change_hardware_layer.sh +++ b/webserver/scripts/change_hardware_layer.sh @@ -136,6 +136,13 @@ elif [ "$1" == "piplc" ]; then echo rpi > ../scripts/openplc_platform echo piplc > ../scripts/openplc_driver +elif [ "$1" == "synergy_logic" ]; then + echo "Activating Synergy Logic driver" + cp ./hardware_layers/synergy.cpp ./hardware_layer.cpp + echo "Setting Platform" + echo linux > ../scripts/openplc_platform + echo synergy_logic > ../scripts/openplc_driver + else echo "Error: Invalid hardware layer" fi diff --git a/webserver/webserver.py b/webserver/webserver.py index 7823b6d..06f89c3 100644 --- a/webserver/webserver.py +++ b/webserver/webserver.py @@ -1778,6 +1778,8 @@ def hardware(): else: return_str += "" if (current_driver == "piplc"): return_str += "" else: return_str += "" + if (current_driver == "synergy_logic"): return_str += "" + else: return_str += "" return_str += """