package fcs

import java.util.Properties;
import org.lsst.ccs.bootstrap.BootstrapResourceUtils;
import org.lsst.ccs.subsystems.fcs.*
import org.lsst.ccs.subsystems.fcs.testbench.*
import org.lsst.ccs.subsystems.fcs.common.*
import org.lsst.ccs.subsystems.fcs.simulation.*
import org.lsst.ccs.subsystems.fcs.drivers.*
import org.lsst.ccs.monitor.Channel
import org.lsst.ccs.monitor.Device
import org.lsst.ccs.description.groovy.CCSBuilder
import org.lsst.ccs.drivers.canopenjni.*
import org.lsst.ccs.drivers.canopenjni.rmi.*
import org.lsst.ccs.bootstrap.SubstitutionTokenUtils;

//************************************************************************//
// subsystem properties
// org.lsst.ccs.run.mode can be "simulation" or "hardware"
// org.lsst.ccs.fcs.instance.autochanger can be "PROTO", "AC1" or "AC2"
// org.lsst.ccs.fcs.instance.loader can be "PROTO", "LO1" or "LO2"
//************************************************************************//
Properties props = BootstrapResourceUtils.getBootstrapSystemProperties();



def runMode = props.getProperty("org.lsst.ccs.run.mode", "simulation");
def instanceAC = props.getProperty("org.lsst.ccs.fcs.instance.autochanger", "AC1");
def instanceCA = props.getProperty("org.lsst.ccs.fcs.instance.carousel", "FINAL");
def instanceLO = props.getProperty("org.lsst.ccs.fcs.instance.loader", "LO1");
boolean standaloneAutochanger = props.getProperty("org.lsst.ccs.fcs.instance.autochanger.standalone", "false").equals("true");
boolean standaloneCarousel = props.getProperty("org.lsst.ccs.fcs.instance.carousel.standalone", "false").equals("true");
boolean standaloneLoader = props.getProperty("org.lsst.ccs.fcs.instance.loader.standalone", "false").equals("true");

//for fullFcs at SLAC until we get rid of rmiserver
def hcuIP = props.getProperty("org.lsst.ccs.fcs.hcu.ip", "quadbox");

//lsst-lion18:134.79.209.94
//quadbox:134.79.209.64 (lsst-lion03)
//lpnhe:134.158.154.104

def registryHostName
if ( "quadbox".equals(hcuIP)) {
    registryHostName = "134.79.209.64";
} else if ( "lion18".equals(hcuIP) ) {
    registryHostName = "134.79.209.94";
} else if ( "lpnhe".equals(hcuIP) ) {
    registryHostName = "134.158.154.104";
} else {
    throw new RuntimeException("Wrong value for hcuIP; should be : quadbox, lion18 or lpnhe");
}

//Get the list of filters from the "build" category configuration files
List filters = buildPropertyValue("org.lsst.ccs.subsystem.fcs.filter.manager.filters", List.class, null);

int standaloneCounter = 0;
if ( standaloneAutochanger ) standaloneCounter++;
if ( standaloneCarousel ) standaloneCounter++;
if ( standaloneLoader ) standaloneCounter++;

if ( standaloneCounter > 1 ) {
    throw new RuntimeException("Something went wrong with the standalone configuration: autochanger("+standaloneAutochanger+") carousel("+standaloneCarousel+") loader("+standaloneLoader+")");
}

boolean fullFcs = standaloneCounter == 0;

if ( fullFcs ) {
    System.out.println("Configuring groovy for full FCS");
} else if ( standaloneAutochanger ) {
    System.out.println("Configuring groovy for Autochanger standalone");    
} else if ( standaloneCarousel ) {
    System.out.println("Configuring groovy for Carousel standalone");    
} else if ( standaloneLoader ) {
    System.out.println("Configuring groovy for Loader standalone");    
}

if ( fullFcs && !runMode.equals("simulation")) {
    System.out.println("FCS is running on " + hcuIP);
}

//************************************************************************//

CCSBuilder builder = ["fcs"]

//********************************************************************************//
//Carousel variables
//********************************************************************************//

/* numOfAnalogInput on the CANopen device where we read the clamps sensors*/
def numOfAnalogInputFilterPresenceSensorXminus = [1, 2, 3, 4, 5] as int[]
def numOfAnalogInputLockSensorXminus = [6, 7, 8, 9, 10] as int[]
def numOfAnalogInputFilterPresenceSensorXplus = [11, 12, 13, 14, 15] as int[]
def numOfAnalogInputLockSensorXplus = [16, 17, 18, 19, 20] as int[]


//Carousel clamps controllers name
//if names are changed, change also in class FCSCst
def clampXminusControllerName = "clampXminusController" //controller to unlock clamp on the side Xminus
def clampXplusControllerName =  "clampXplusController" //controller to unlock clamp on the side Xplus
//********************************************************************************//

//********************************************************************************//
//Autochanger variables
//********************************************************************************//
//if names are changed, change also in class FCSCst
def acPlutoGatewayName = "acSensorsGateway" // to monitor the autochanger sensors
//************************************************************************//
//In FINAL products AC1 and AC2 on linear rails, 
//truckX+  is controlled by driver controller
//and truckX- is controlled by follower controller.
//On prototype truckX+ is controlled by follower and X- by driver.
//************************************************************************//
//if names are changed, change also in class FCSCst
def acTruckXminusControllerName = "acTruckXminusController"
def acTruckXplusControllerName = "acTruckXplusController"
//********************************************************************************//

//********************************************************************************//
//Loader variables
//********************************************************************************//
//if names are changed, change also in class FCSCst
def bridgeToLoaderName = "canbus1" 
def loaderPlutoGatewayName = "loaderPlutoGateway" // to monitor the loader sensors
//********************************************************************************//

Class carouselClass = Class.forName("org.lsst.ccs.subsystems.fcs." + (runMode.equals("simulation") ? "simulation.Simu" : "") + "Carousel");
Class carouselSocketClass = Class.forName("org.lsst.ccs.subsystems.fcs." + (runMode.equals("simulation") ? "simulation.Simu" : "") + "CarouselSocket");
Class autochangerClass = Class.forName("org.lsst.ccs.subsystems.fcs." + (runMode.equals("simulation") ? "simulation.Simu" : "") + "Autochanger");
Class autochangerTwoTrucksClass = Class.forName("org.lsst.ccs.subsystems.fcs." + (runMode.equals("simulation") ? "simulation.Simu" : "") + "AutochangerTwoTrucks");
Class loaderClass = Class.forName("org.lsst.ccs.subsystems.fcs." + (runMode.equals("simulation") ? "simulation.Simu" : "") + "Loader");

Class mainClass;
if (fullFcs) {
    mainClass = org.lsst.ccs.subsystems.fcs.FcsMain.class;
} else if (standaloneAutochanger) {
    mainClass = AutochangerMain.class;
} else if (standaloneCarousel) {
    mainClass = CarouselMain.class;
} else if (standaloneLoader) {
    mainClass = LoaderMain.class;
}

System.out.println("Building Main class: "+mainClass);

builder.
    main(mainClass)
    { //begin description of Main subs
    
    //-------------------------
    // BEGIN Filter Manager description
    if ( fullFcs || standaloneCarousel || standaloneAutochanger) {
        filterManager(FilterManager) {
            //begin description of filter manager's children
            for ( String filter : filters ) {
                "$filter" (Filter)
            }
    
        }
    } 
    ////end description of filter manager's children
    // END Filter Manager description
    // ------------------------------
    
    //-------------------------
    if ( fullFcs || standaloneAutochanger || standaloneCarousel ) {
        canbus0 (CanOpenProxy) 
        { 
            
            Class canInterfaceClass = runMode.equals("simulation") ? SimuCanOpenInterface.class : SocketCanOpen.class;
            //to launch fcs on the same HCU than canfestival
            canInterface(canInterfaceClass)
            
            if ( fullFcs || standaloneCarousel ) {
                Class hyttc580Class = runMode.equals("simulation") ? SimuTTC580.class : CanOpenTTC580.class;        
                // can open devices to read clamps sensors
                hyttc580 (hyttc580Class)
                
                Class carouselClampControllerClass = runMode.equals("simulation") ? SimuCarouselClampController.class : CanOpenEPOSCarouselClamp.class;
                "$clampXminusControllerName" (carouselClampControllerClass)
                "$clampXplusControllerName" (carouselClampControllerClass)
                
                Class carouselControllerClass = runMode.equals("simulation") ? SimuCarouselController.class : CanOpenEPOSCarousel.class;
                carouselController (carouselControllerClass)

                Class ai814Class = runMode.equals("simulation") ? SimuCanOpenADC.class : CanCBXAI814.class;
                // can open devices to read brakes sensors and temperatures
                ai814 (ai814Class)
                
                // can open devices to read temperatures in carousel motor and brakes
                pt100 (CanOpenPT100)
                
                //accelerometer on the back flange
                Class acceleroClass = runMode.equals("simulation") ? SimuMG550P.class : CanOpenMG550P.class;
                accelerobf (acceleroClass)
                
                if ( standaloneCarousel && "simulation".equals(runMode)) {
                        //fake plutoGateway to simulate autochanger signals
                        fakePlutoGateway (SimuCarouselStandalonePlutoGateway)
                }
            }
            
            if ( fullFcs || standaloneAutochanger ) {
                
                //This is for the autochanger in the fcs
                Class autochangerPlutoGatewayClass = runMode.equals("simulation") ? (fullFcs ? SimuAutochangerPlutoGateway.class : SimuAutochangerStandalonePlutoGateway.class ) : CanOpenPlutoGateway.class;
                // can open devices
                //sensors gateway
                "$acPlutoGatewayName" (autochangerPlutoGatewayClass)
                
                Class strainClass = runMode.equals("simulation") ? SimuCanOpenStrainGauge.class : CanOpenStrainGauge.class;
                onlineStrainGauge (strainClass)
                
                Class linearControllerClass = runMode.equals("simulation") ? SimuAutochangerLinearRailController.class : CanOpenEPOSLinearRailTruck.class;           
                // can open motor controllers
                "$acTruckXminusControllerName" (linearControllerClass)
                "$acTruckXplusControllerName" (linearControllerClass)     
                
                Class latchControllerClass = runMode.equals("simulation") ? SimuAutochangerLatchController.class : CanOpenEPOS.class;
                latchXminusController (latchControllerClass)
                latchXplusController (latchControllerClass)
                
                
                Class autochangerClampControllerClass = runMode.equals("simulation") ? SimuAutochangerOnlineClampController.class : CanOpenEPOSOnlineClamp.class;
                onlineClampXminusController (autochangerClampControllerClass)
                onlineClampXplusController (autochangerClampControllerClass)
                onlineClampYminusController (autochangerClampControllerClass)
                
                //Devices which are the same classes for hardware and for simulation
                brakeSystemGateway (CanOpenPlutoGateway)
                tempSensorsDevice1 (CanOpenSeneca4RTD)
                tempSensorsDevice2 (CanOpenSeneca4RTD)        

                Class proximitySensorsClass = runMode.equals("simulation") ? SimuCanCBXAI420.class : CanCBXAI420.class;
                proximitySensorsDevice (proximitySensorsClass)      
            }
            //---- DONE AUTOCHANGER
            
        }
    }
    
    //
    ////end description of bridgeToChanger's children
    //----------------------------------------------
    // END Bridge To CHANGER description
    //----------------------------------------------                
    //
    // tcp Proxy LOADER ONLY
    
    if ( fullFcs || standaloneLoader ) {
        Class canopenProxyClass = runMode.equals("simulation") ? SimuLoaderCanOpenProxy.class : LoaderCanOpenProxy.class;        
        "$bridgeToLoaderName"(canopenProxyClass) 
        {
            //real hardware
            if ( runMode.equals("hardware") && fullFcs ) { 
                //hardware at SLAC
                //test in January 2020 the following line doesn't work because an init of loaderCanInterface init the cahnger canInterface.
                //            loaderCanInterface(CanFestivalJNI)

                //loaderCanInterface(CanOpenRMIClient, registryHostName:"$registryHostName", bindName:"canbus1")
                loaderCanInterface(SocketCanOpen)
            } else if ( runMode.equals("hardware") && standaloneLoader ) { 
               // loaderCanInterface(LelyJNI)
                loaderCanInterface(SocketCanOpen)
            } else {            
                loaderCanInterface(SimuCanOpenInterface)
            }
            
            Class loaderPlutoGatewayClass = runMode.equals("simulation") ? SimuLoaderStandalonePlutoGateway.class : CanOpenPlutoGateway.class;
            "$loaderPlutoGatewayName" (loaderPlutoGatewayClass)
            
            
            Class loaderHooksControllerClass = runMode.equals("simulation") ? SimuLoaderClampController.class : CanOpenEPOS.class;
            hooksController (loaderHooksControllerClass) 
            
            Class loaderCarrierControllerClass = runMode.equals("simulation") ? SimuLoaderCarrierController.class : CanOpenEPOS.class;
            carrierController (loaderCarrierControllerClass)            
            
            
        }
    }
    //end description of bridge's childrens
    //----------------------------------------------
    // END Bridge To LOADER description
    //---------------------------------------------
    
    
    if ( fullFcs || standaloneAutochanger ) {
        // BEGIN Filter Identificator description
        filterIdentificator(FilterIdentificator)
        {//begin description of filter Identificator's children
            filterIDSensor0 (DigitalSensor)
            
            filterIDSensor1 (DigitalSensor)
            
            filterIDSensor2 (DigitalSensor)
            
            filterIDSensor3 (DigitalSensor)
            
            filterIDSensor4 (DigitalSensor)
            
            filterIDSensor5 (DigitalSensor)
        }//end description of filter identificator's children
        // END Filter Identificator description
    }
    
    /**********************************************************************/
    /*                BEGIN OF CAROUSEL DESCRIPTION                       */
    /**********************************************************************/

    //----------------------------
    // BEGIN Carousel description
    if ( fullFcs || standaloneCarousel ) {
        carousel(carouselClass)
        {//begin description of carousel's children
            /**********************************************************************/
            /*SOCKETi*/
            for (int i = 1; i<6;i++ ) {
            "socket${i}" ( carouselSocketClass,
                    id:i,
                    clampXminus:ref("clampXminus${i}"),
                    clampXplus:ref("clampXplus${i}"),
                    ioModuleSensor:ref("ioModuleSensor${i}")
                )
                {   
                "ioModuleSensor${i}" (CarouselSensor)

                "clampXminus${i}" (CarouselClamp)
                    
                "clampXplus${i}" (CarouselClamp)
                    
                } // END carouselSocket /*SOCKETi*/ description
            } // END of loop of carouselSocket description.

            plc(PLC)
            {//begin description of carousel PLC
                //Signals read on hyttc580 for PLC debug (PLCCarouselPanel)
                caCS (CarouselPLCSensor)
                caCSb (CarouselPLCSensor)
                caCFC (CarouselPLCSensor)
                caCFCb (CarouselPLCSensor)
                caCF0 (CarouselPLCSensor)
                caCF0b (CarouselPLCSensor)
                caCF1 (CarouselPLCSensor)
                caCF1b (CarouselPLCSensor)
                caAP1 (CarouselPLCSensor)
                caAP1b (CarouselPLCSensor)
                caAP2 (CarouselPLCSensor)
                caAP2b (CarouselPLCSensor)
                caAP3 (CarouselPLCSensor)
                caAP3b (CarouselPLCSensor)
                caAF3 (CarouselPLCSensor)
                caAF3b (CarouselPLCSensor)
                caEnableBrakes  (CarouselPLCSensor)
                caEnableRotation (CarouselPLCSensor)
                caEnableUnclamp (CarouselPLCSensor)
                caEnableShutter (CarouselPLCSensor)
                tpStopUnclamp (CarouselPLCSensor)
                tpStopRotation (CarouselPLCSensor)
                tpCheckRotation (CarouselPLCSensor)
                caLockout (CarouselPLCSensor)
                caLockoutb (CarouselPLCSensor)
                caEng (CarouselPLCSensor)
                caEngb (CarouselPLCSensor)
                caBrakesActivated (CarouselPLCSensor)
                caSleep (CarouselPLCSensor)
                caShutterInactive (CarouselPLCSensor)
                okDI (CarouselPLCSensor)
                okDIsafety (CarouselPLCSensor)
                enableShutterInterlock (CarouselPLCSensor)
                powerSave (CarouselPLCSensor)
            }//end description of carousel PLC
        }
    } else if ( standaloneAutochanger ) {        
        /**********************************************************************/
        /*                FAKE CAROUSEL DESCRIPTION                       */
        /**********************************************************************/
        //in the autochanger standalone subsystem the fake carousel is a switch which
        //simulates that carousel holds a filter at STANDBY 
        carousel(FakeFilterHolder, holdingFilterSensorName:"carouselHoldingFilterSensors") // CFC
        /**********************************************************************/
        /*                END of FAKE CAROUSEL DESCRIPTION                 */
        /**********************************************************************/        
    }
    //end description of carousel's children
    //END Carousel description
    // ------------------------    
    
    
    if ( fullFcs || standaloneAutochanger ) {
        // ------ Monitoring 
        seneca1(TempMonitorDevice, tempSensorsDevice:ref("tempSensorsDevice1"))
        seneca2(TempMonitorDevice, tempSensorsDevice:ref("tempSensorsDevice2"))
        
        // Channels read as a group with getADCValues
        "autochanger/temperatures/tempLinearRailMotorXplus"(Channel, devcName:"seneca1", hwChan:1, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        "autochanger/temperatures/tempLinearRailMotorXminus"(Channel, devcName:"seneca1", hwChan:2, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        "autochanger/temperatures/tempClampMotorXplus"(Channel, devcName:"seneca1", hwChan:3, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        "autochanger/temperatures/tempClampMotorYminus"(Channel, devcName:"seneca1", hwChan:4, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        
        "autochanger/temperatures/tempClampMotorXminus"(Channel, devcName:"seneca2", hwChan:1, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        "autochanger/temperatures/tempFrontBox"(Channel, devcName:"seneca2", hwChan:2, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        "autochanger/temperatures/tempRearBox"(Channel, devcName:"seneca2", hwChan:3, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
        "autochanger/temperatures/tempCellXminus"(Channel, devcName:"seneca2", hwChan:4, units:"\u00b0C", format:".1f", type:"C", subtype:"", checkLo:"flag", alarmLo:null, checkHi:"flag", alarmHi:null)
    }
    //**************************************************************************//
    //**************************************************************************//
    // autochanger
    //**************************************************************************//
    //**************************************************************************//
    if ( standaloneCarousel ) {
        /**********************************************************************/
        /*                FAKE AUTOCHANGER DESCRIPTION                       */
        /**********************************************************************/
        //in the carousel standalone subsystem we need an autochanger which is a FilterHolder

        autochanger(FakeFilterHolderForCarouselStandalone)
    } else if ( standaloneLoader ) {  
        /**********************************************************************/
        /*                FAKE AUTOCHANGER DESCRIPTION                       */
        /**********************************************************************/
        //in the loader standalone subsystem the fake autochanger is a switch which
        //simulate that autochanger holds a filter at HANDOFF 
        
        /**********************************************************************/
        /*                END of FAKE AUTOCHANGER DESCRIPTION                 */
        /**********************************************************************/
        autochanger(FakeFilterHolder, holdingFilterSensorName:"acAF3", notHoldingFilterSensorName:"acAF0")
    } else {
        autochanger(autochangerClass, plutoGateway:ref("$acPlutoGatewayName")) {
            ////begin description of autochanger children
            
            //**************************************************************************//
            // autochanger trucks description
            //**************************************************************************//
            autochangerTrucks (autochangerTwoTrucksClass, 
                truckXminus:ref("acTruckXminus"), truckXplus:ref("acTruckXplus"),
                acTruckXminusController:ref("$acTruckXminusControllerName"),
                acTruckXplusController:ref("$acTruckXplusControllerName")) {
                //begin description of autochanger trucks children
                acTruckXminus (AutochangerTruck, 
                    controller:ref("$acTruckXminusControllerName"),
                    handoffPositionSensors:ref("handoffPositionSensorsXminus"),
                    onlinePositionSensors:ref("onlinePositionSensorsXminus"),
                    standbyPositionSensors:ref("standbyPositionSensorsXminus"),
                )
                {//begin description of autochanger truckXminus children
                    handoffPositionSensorsXminus (ComplementarySensors) { 
                        handoffPositionSensorXminus (DigitalSensor)                        
                        handoffPositionSensorBXminus (DigitalSensor)
                    }
                    onlinePositionSensorsXminus (ComplementarySensors) { 
                        onlinePositionSensorXminus (DigitalSensor)                        
                        onlinePositionSensorBXminus (DigitalSensor)
                    }
                    standbyPositionSensorsXminus (ComplementarySensors) { 
                        standbyPositionSensorXminus (DigitalSensor)                        
                        standbyPositionSensorBXminus (DigitalSensor)
                    }
                }//end description of autochanger acTruckXminus children
                acTruckXplus (AutochangerTruck, 
                    controller:ref("$acTruckXplusControllerName"),
                    handoffPositionSensors:ref("handoffPositionSensorsXplus"),
                    onlinePositionSensors:ref("onlinePositionSensorsXplus"),
                    standbyPositionSensors:ref("standbyPositionSensorsXplus"),
                )
                {//begin description of autochanger truckXplus children
                    handoffPositionSensorsXplus (ComplementarySensors) { 
                        handoffPositionSensorXplus (DigitalSensor)                                    
                        handoffPositionSensorBXplus (DigitalSensor)
                    }
                    onlinePositionSensorsXplus (ComplementarySensors) { 
                        onlinePositionSensorXplus (DigitalSensor)
                        onlinePositionSensorBXplus (DigitalSensor)
                    }
                    standbyPositionSensorsXplus (ComplementarySensors) { 
                        standbyPositionSensorXplus(DigitalSensor)
                        standbyPositionSensorBXplus (DigitalSensor)
                    }
                }//end description of autochanger acTruckXplus children
                
            }//end description of autochanger trucks children            
            //**************************************************************************//
            // end of autochanger trucks description
            //**************************************************************************//
            
            //**************************************************************************//
            // autochanger latches description
            //**************************************************************************//
            latches (AutochangerTwoLatches, latchXminus:ref("latchXminus"), latchXplus:ref("latchXplus"))
            {//begin description of autochanger latches children
                //*************************************//
                // autochanger latchXminus description
                //*************************************//
                latchXminus (AutochangerLatch,
                    latchController:ref("latchXminusController"),
                    closeSensors:ref("closeSensorsLatchXminus"),
                    openSensors:ref("openSensorsLatchXminus"),
                    filterEngagedSensors:ref("filterEngagedSensorsLatchXminus"),
                )
                {//begin description of autochanger latchXminus children
                    closeSensorsLatchXminus (ComplementarySensors) { //begin description of autochanger lockSensorslatchXminus children
                        closeSensorLatchXminus (DigitalSensor)
                        closeSensorBLatchXminus (DigitalSensor)
                    }//end description of autochanger lockSensorslatchXminus children                
                    
                    openSensorsLatchXminus (ComplementarySensors) { //begin description of autochanger unlockSensorslatchXminus children
                        openSensorLatchXminus (DigitalSensor)
                        openSensorBLatchXminus (DigitalSensor)
                    }//end description of autochanger unlockSensorslatchXminus children
                    
                    filterEngagedSensorsLatchXminus (ComplementarySensors) { //begin description of autochanger filterEngagedSensorslatchXminus children
                        filterEngagedSensorLatchXminus (DigitalSensor)
                        filterEngagedSensorBLatchXminus (DigitalSensor)
                    }//end description of autochanger filterEngagedSensorslatchXminus children
                }//end description of autochanger latchXminus children
                //**********************************************//
                // end of autochanger latchXminus description   //
                //**********************************************//
                //*************************************//
                // autochanger latchXplus description
                //*************************************//
                latchXplus (AutochangerLatch,
                    latchController:ref("latchXplusController"),
                    closeSensors:ref("closeSensorsLatchXplus"),
                    openSensors:ref("openSensorsLatchXplus"),
                    filterEngagedSensors:ref("filterEngagedSensorsLatchXplus"),
                )
                {//begin description of autochanger latchXplus children
                    
                    closeSensorsLatchXplus (ComplementarySensors) { //begin description of autochanger lockSensorslatchXplus children
                        closeSensorLatchXplus (DigitalSensor)
                        closeSensorBLatchXplus (DigitalSensor)
                    }//end description of autochanger lockSensorslatchXplus children
                    openSensorsLatchXplus (ComplementarySensors) { //begin description of autochanger unlockSensorslatchXplus children
                        openSensorLatchXplus (DigitalSensor)
                        openSensorBLatchXplus (DigitalSensor)
                    }//end description of autochanger unlockSensorslatchXplus children
                    
                    filterEngagedSensorsLatchXplus (ComplementarySensors) { //begin description of autochanger filterEngagedSensorslatchXplus children
                        filterEngagedSensorLatchXplus (DigitalSensor)
                        filterEngagedSensorBLatchXplus (DigitalSensor)
                    }//end description of autochanger filterEngagedSensorslatchXplus children
                    
                }//end description of autochanger latchXplus children
                //**********************************************//
                // end of autochanger latchXplus description   //
                //**********************************************//
            }//end description of autochanger latches children
            //*****************************************************************************//
            // end of autochanger latches description
            //*****************************************************************************//
            
            //**************************************************************************//
            //**************************************************************************//
            // autochanger onlineClamps description
            //**************************************************************************//
            //**************************************************************************//
            onlineClamps (AutochangerThreeOnlineClamps, 
                onlineClampXminus:ref("onlineClampXminus"), 
                onlineClampXplus:ref("onlineClampXplus"), 
                onlineClampYminus:ref("onlineClampYminus")) 
            {//begin description of autochanger onlineClamps children
                
                onlineClampXminus(AutochangerOnlineClamp ,
                    controller:ref("onlineClampXminusController"),
                    closeSensors:ref("onlineClampXminusCloseSensors"),
                    openSensors:ref("onlineClampXminusOpenSensors"),
                )
                {//begin description of autochanger onlineClampXminus children
                    onlineClampXminusCloseSensors (ComplementarySensors) { //begin description of autochanger onlineClampXminusCloseSensors children
                        onlineClampXminusCloseSensor (DigitalSensor)
                        onlineClampXminusCloseSensorC (DigitalSensor)
                    }//end description of autochanger onlineClampXminusCloseSensors children
                    
                    onlineClampXminusOpenSensors (ComplementarySensors) { 
                        onlineClampXminusOpenSensor (DigitalSensor)
                        onlineClampXminusOpenSensorC (DigitalSensor)  
                    }
                }//end of  description of autochanger onlineClampXminus children
                
                onlineClampXplus(AutochangerOnlineClamp ,
                    controller:ref("onlineClampXplusController"),
                    closeSensors:ref("onlineClampXplusCloseSensors"),
                    openSensors:ref("onlineClampXplusOpenSensors"),
                )
                {//begin description of autochanger onlineClampXplus children
                    onlineClampXplusCloseSensors (ComplementarySensors) { 
                        onlineClampXplusCloseSensor (DigitalSensor)
                        onlineClampXplusCloseSensorC (DigitalSensor)
                    }                    
                    onlineClampXplusOpenSensors (ComplementarySensors) { 
                        onlineClampXplusOpenSensor (DigitalSensor)
                        onlineClampXplusOpenSensorC (DigitalSensor)
                    }
                }//end of  description of autochanger onlineClampXplus children
                
                onlineClampYminus(AutochangerOnlineClamp ,
                    controller:ref("onlineClampYminusController"),
                    closeSensors:ref("onlineClampYminusCloseSensors"),
                    openSensors:ref("onlineClampYminusOpenSensors"),
                )
                {//begin description of autochanger onlineClampYminus children
                    onlineClampYminusCloseSensors (ComplementarySensors) { 
                        onlineClampYminusCloseSensor (DigitalSensor)
                        onlineClampYminusCloseSensorC (DigitalSensor)
                    }
                    onlineClampYminusOpenSensors (ComplementarySensors) { 
                        onlineClampYminusOpenSensor (DigitalSensor)
                        onlineClampYminusOpenSensorC (DigitalSensor)
                    }
                }//end of  description of autochanger onlineClampYminus children
            }//end of  description of autochanger onlineClamps children
            //**************************************************************************//
            // end of autochanger onlineClamps description
            //**************************************************************************//

            plc(PLC) 
            {//begin description of autochanger PLC
                // *******************************************
                // * AUTOCHANGER LOCAL PROTECTION SYSTEM     *
                // *******************************************

                //Carousel_CFC signal read on AC pluto Gateway sent by carousel.
                carouselHoldingFilterSensors(ComplementarySensors) {		
                    carouselHoldingFilterSensor0(DigitalSensor)		
                    carouselHoldingFilterSensor1(DigitalSensor)		
                }

                //Signals read on AC pluto gateway sent by loader
                // 
                loaderHoldingFilterSensors(ComplementarySensors) { //Loader_LRH (ex LFL)		
                    loaderHoldingFilterSensor0(DigitalSensor)		
                    loaderHoldingFilterSensor1(DigitalSensor)    		
                }

                loaderConnectedSensors(ComplementarySensors) {  // Loader_LPS
                    loaderConnectedSensor(DigitalSensor)		
                    loaderConnectedSensorC(DigitalSensor)		
                }//end description of autochanger loaderConnectedSensors children

                //Carousel_CS
                carouselStoppedAtStandbySensors(ComplementarySensors) { // Carousel_CS
                    carouselStoppedAtStandby(DigitalSensor)		
                    carouselStoppedAtStandbyC(DigitalSensor)
                }//end description of autochanger carouselStoppedAtStandbySensors children

                carousel_CF0Sensors(ComplementarySensors) { // Carousel_CF0
                    carousel_CF0(DigitalSensor)		
                    carousel_CF0_C(DigitalSensor)
                }//end description of autochanger carousel_CF0 children 

                carousel_CF1Sensors(ComplementarySensors) { // Carousel_CF1
                    carousel_CF1(DigitalSensor)		
                    carousel_CF1_C(DigitalSensor)
                }//end description of autochanger carousel_CF1 children 

                // Local Protection Module status
                lpmLinearRail1Status(DigitalSensor) 

                lpmLinearRail2Status(DigitalSensor)        

                lpmOnlineClampsStatus(DigitalSensor)

                lpmLatchesStatus(DigitalSensor)

                //engineeringKeySensors
                lpmEngineeringKeySensors (ComplementarySensors) { 
                    engineeringKey (DigitalSensor)
                    engineeringKey_C (DigitalSensor)
                } // end description loaderPresenceSensors

                //loaderPresenceSensors is only on AC1 and AC2, not on PROTO
                loaderPresenceSensors (ComplementarySensors) { 
                    presenceLoader (DigitalSensor)
                    presenceLoader_C (DigitalSensor)
                } // end description loaderPresenceSensors

                //lockOutSensors is only on AC1 and AC2, not on PROTO
                lockOutSensors (ComplementarySensors) { 
                    lockOut (DigitalSensor)	
                    lockOut_C (DigitalSensor)
                } // end description lockOutSensors 

                //lockOutShunt is only on AC1 and AC2, not on PROTO
                lockOutShunt (DigitalSensor)

                //Signal emitted by autochanger OUT_AF0
                OUT_AF0_Sensors (ComplementarySensors) { 
                    OUT_AF0 (DigitalSensor)		
                    OUT_AF0_C (DigitalSensor)
                } // end description OUT_AF0      

                //Signal emitted by autochanger OUT_AF1
                OUT_AF1_Sensors (ComplementarySensors) { 
                    OUT_AF1 (DigitalSensor)		
                    OUT_AF1_C (DigitalSensor)
                } // end description OUT_AF1     

                //Signal emitted by autochanger OUT_AF3
                OUT_AF3_Sensors (ComplementarySensors) { 
                    OUT_AF3 (DigitalSensor)		
                    OUT_AF3_C (DigitalSensor)
                } // end description OUT_AF3    

                //Signal emitted by autochanger OUT_AP1
                OUT_AP1_Sensors (ComplementarySensors) { 
                    OUT_AP1 (DigitalSensor)		
                    OUT_AP1_C (DigitalSensor)
                } // end description OUT_AP1 

                //Signal emitted by autochanger OUT_AP2
                OUT_AP2_Sensors (ComplementarySensors) { 
                    OUT_AP2 (DigitalSensor)		
                    OUT_AP2_C (DigitalSensor)
                } // end description OUT_AP2

                //Signal emitted by autochanger OUT_AP3
                OUT_AP3_Sensors (ComplementarySensors) { 
                    OUT_AP3 (DigitalSensor)		
                    OUT_AP3_C (DigitalSensor)
                } // end description OUT_AP3  

                //Signal emitted by autochanger OUT_AIN
                OUT_AIN (DigitalSensor) 

                //Signal emitted by autochanger OUT_AOL
                OUT_AOL (DigitalSensor) 

                inclinometerXminus (Inclinometer)
                inclinometerXplus (Inclinometer)

            }//end description of autochanger PLC
            
            
        }//end description of autochanger children
        //*****************************************************************************//
        //*****************************************************************************//
        // end of autochanger description
        //*****************************************************************************//
        //*****************************************************************************//
    }    
    
    if ( standaloneAutochanger ) {
        /**********************************************************************/
        /*                FAKE LOADER DESCRIPTION                       */
        /**********************************************************************/
        //in the autochanger standalone subsystem the fake loader is couple of complementary sensors
        //simulate that loader holds a filter at HANDOFF     
        loader(FakeFilterHolder, holdingFilterSensorName:"loaderHoldingFilterSensors")
        /**********************************************************************/
        /*                END of FAKE LOADER DESCRIPTION                 */
        /**********************************************************************/        
    } else if (fullFcs || standaloneLoader) {
        //begin description of loader
        loader (loaderClass)
        {//begin description of loader's children
            
            carrier(LoaderCarrier)
            {//begin description of loader carrier's children                
             
                if ( "PROTO".equals(instanceLO)) {
                    loaderEngagedSensors (RedondantSensors) {//begin description of loader engagedSensors
                        loaderEngagedPositionSensor0 (DigitalSensor)                        
                        loaderEngagedPositionSensor1 (DigitalSensor)
                    }//end description of loader engagedSensors
                    
                    loaderHandoffSensors (RedondantSensors) {//begin description of loader handoffSensors
                        loaderHandoffPositionSensor0 (DigitalSensor)                        
                        loaderHandoffPositionSensor1 (DigitalSensor)
                    }//end description of loader handoffSensors   
                    
                } else {                
                    loaderEngagedSensors (RedondantSensors) {//begin description of loader engagedSensors
                        loaderEngagedPositionSensor0 (DigitalSensor)                        
                        loaderEngagedPositionSensor1 (DigitalSensor)
                    }//end description of loader engagedSensors
                    
                    loaderHandoffSensors (RedondantSensors) {//begin description of loader handoffSensors
                        loaderHandoffPositionSensor0 (DigitalSensor)                        
                        loaderHandoffPositionSensor1 (DigitalSensor)
                    }//end description of loader handoffSensors                    
                }
                
                loaderStorageSensors (RedondantSensors) {//begin description of loader storageSensors
                    //user_to_gateway n° 1
                    loaderStoragePositionSensor0 (DigitalSensor)                    
                    loaderStoragePositionSensor1 (DigitalSensor)  
                }//end description of loader storageSensors
                
            }//end description of loader carrier's children
            
            hooks(LoaderClamp,
                hook1:ref("hook1"),hook2:ref("hook2"), hook3:ref("hook3"),hook4:ref("hook4"),
                forceSensor0:ref("forceSensor0"), forceSensor1:ref("forceSensor1"),
                clampedStatusSensor: ref("clampedStatusSensor"),
            )
            {//begin description of loader clamp's children
                for (int i = 1; i<5; i++) {
                    
            "hook${i}"(LoaderHook,
                        closeSensor:ref("loaderCloseSensor${i}"), openSensor:ref("loaderOpenSensor${i}"))
                    { //user_to_gateway n° 1
                    "loaderCloseSensor${i}" (DigitalSensor)
                        
                    "loaderOpenSensor${i}" (DigitalSensor)
                    }
                }
                
                // user_to_gateway n° 2 (byteNumero = 4)
                forceSensor0(ForceSensor)
                
                // user_to_gateway n° 3 (byteNumero = 8)
                forceSensor1(ForceSensor)
                
                // user_to_gateway n° 2
                unclampedStatusSensor (DigitalSensor)
                
                underClampedStatusSensor (DigitalSensor)
                
                clampedStatusSensor (DigitalSensor) 
                
                overClampedStatusSensor (DigitalSensor)
                
            }//end description of loader clamp's children
            
            plc(PLC)
            {//begin description of loader plc.
                loaderFilterPresenceSensors (RedondantSensors) { // user_to_gateway n° 1
                    loaderFilterPresenceSensor0(DigitalSensor)                
                    loaderFilterPresenceSensor1(DigitalSensor) 
                }

                loaderOnCameraSensors (RedondantSensors) { // user_to_gateway n° 1
                    loaderOnCameraSensor0(DigitalSensor)                
                    loaderOnCameraSensor1(DigitalSensor)
                }

                //key lock or permit signal
                keyLockSensors (ComplementarySensors) { 
                    keyLock (DigitalSensor)
                    keyLockb (DigitalSensor)
                }

                //engineering key
                keyEngSensors (ComplementarySensors) { 
                    keyEng (DigitalSensor)
                    keyEngb (DigitalSensor)
                }

                // output signals for Camera Protection System
                //LRH gateway 3, bits 4 & 5
                //LPS gateway 3, bits 6 & 7
                // LRH (Loader Holding Filter at HANDOFF)
                Loader_LRH (ComplementarySensors) { 
                    LRH_0 (DigitalSensor)
                    LRH_1 (DigitalSensor)
                }

                // LPS (Loader at STORAGE)
                Loader_LPS (ComplementarySensors) { 
                    LPS_0 (DigitalSensor)
                    LPS_1 (DigitalSensor)
                }

                // gateway 2 (byteNumero = 6 inputNumero = 6) (on pluto bit 6)
                loaderCarrierRelayStatus (DigitalSensor)

                // gateway 2 (byteNumero = 6 inputNumero = 7) (on pluto bit 7)
                loaderHooksRelayStatus (DigitalSensor) 

                // gateway 2 (byteNumero = 7 inputNumero = 5) (on pluto bit 13)
                loaderChainPresenceSensor (DigitalSensor) 

                // gateway 2 (byteNumero = 7 inputNumero = 6) (on pluto bit 14)
                loaderDefaultStatus (DigitalSensor)

                // gateway 2 (byteNumero = 7 inputNumero = 7) (on pluto bit 15)
                loaderFilterGoodPositionStatus (DigitalSensor)

                // gateway 4 (byteNumero = 14 inputNumero = 0)
                loader_LFS (DigitalSensor)

                // gateway 4 (byteNumero = 14 inputNumero = 1)
                loader_LFD (DigitalSensor)

                // filter measurement distance (0.1 Volts)
                // gateway 1 byte 0 (cf force sensor)
                loaderFilterDistanceSensor (ForceSensor)

                //Signals comming from autochanger and read on loaderPlutoGateway//
                //AP2 user_to_gateway 3 input 0
                //AF0 user_to_gateway 3 input 1
                //AF1 user_to_gateway 3 input 2
                //AF3 user_to_gateway 3 input 3
                acAP2 (ComplementarySensors) { 
                    acAP2s (DigitalSensor)
                    acAP2b (DigitalSensor)
                }

                acAF0 (ComplementarySensors) { 
                    acAF0s (DigitalSensor)
                    acAF0b (DigitalSensor)
                } 

                acAF1 (ComplementarySensors) { 
                    acAF1s (DigitalSensor)
                    acAF1b (DigitalSensor)
                } 

                acAF3 (ComplementarySensors) { 
                    acAF3s (DigitalSensor)
                    acAF3b (DigitalSensor)
                } 
                //end of Signals comming from autochanger and read on loaderPLutoGateway// 
            }// end description of loader plc
            
        }//end description of loader's children
    }
            
} // end description of main (FcsMain)
