All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.simulation.GBMboostMasterSimulator Maven / Gradle / Ivy

There is a newer version: 2.0.0-M1
Show newest version
package org.bidib.wizard.simulation;

import java.util.LinkedHashMap;
import java.util.Map;

import org.bidib.jbidibc.core.BidibLibrary;
import org.bidib.jbidibc.core.Feature;
import org.bidib.jbidibc.core.RcPlusUniqueIdData;
import org.bidib.jbidibc.core.TidData;
import org.bidib.jbidibc.core.enumeration.CommandStationProgState;
import org.bidib.jbidibc.core.enumeration.CommandStationPt;
import org.bidib.jbidibc.core.enumeration.CommandStationState;
import org.bidib.jbidibc.core.enumeration.RcPlusPhase;
import org.bidib.jbidibc.core.exception.ProtocolException;
import org.bidib.jbidibc.core.message.BidibCommand;
import org.bidib.jbidibc.core.message.CommandStationAccessoryAcknowledgeResponse;
import org.bidib.jbidibc.core.message.CommandStationAccessoryMessage;
import org.bidib.jbidibc.core.message.CommandStationDriveAcknowledgeResponse;
import org.bidib.jbidibc.core.message.CommandStationDriveMessage;
import org.bidib.jbidibc.core.message.CommandStationPomAcknowledgeResponse;
import org.bidib.jbidibc.core.message.CommandStationPomMessage;
import org.bidib.jbidibc.core.message.CommandStationProgMessage;
import org.bidib.jbidibc.core.message.CommandStationProgStateResponse;
import org.bidib.jbidibc.core.message.CommandStationRcPlusAcknowledgeResponse;
import org.bidib.jbidibc.core.message.CommandStationRcPlusMessage;
import org.bidib.jbidibc.core.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.core.message.CommandStationStateResponse;
import org.bidib.jbidibc.core.message.FeedbackCvResponse;
import org.bidib.jbidibc.core.message.NodeLostResponse;
import org.bidib.jbidibc.core.message.NodeNewResponse;
import org.bidib.jbidibc.core.message.NodeTabCountResponse;
import org.bidib.jbidibc.core.message.NodeTabResponse;
import org.bidib.jbidibc.core.utils.ByteUtils;
import org.bidib.jbidibc.core.utils.NodeUtils;
import org.bidib.jbidibc.simulation.InterfaceNode;
import org.bidib.jbidibc.simulation.SimulatorNode;
import org.bidib.jbidibc.simulation.SimulatorRegistry;
import org.bidib.jbidibc.simulation.events.NodeAvailableEvent;
import org.bidib.jbidibc.simulation.events.NodeLostEvent;
import org.bidib.jbidibc.simulation.net.SimulationBidibMessageProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GBMboostMasterSimulator extends GBMboostNodeSimulator implements InterfaceNode {
    private static final Logger LOGGER = LoggerFactory.getLogger(GBMboostMasterSimulator.class);

    private static final String SIMULATION_PANEL_CLASS =
        "org.bidib.wizard.mvc.simulation.view.panel.GBMboostMasterPanel";

    private byte localAddrIndex;

    private CommandStationState commandStationState = CommandStationState.OFF;

    private Map mapLocoCV = new LinkedHashMap();

    private TidData tid = new TidData(new RcPlusUniqueIdData(0x0D0C0B0A, 13), 1);

    public GBMboostMasterSimulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
        SimulationBidibMessageProcessor messageReceiver) {
        super(nodeAddress, uniqueId, autoAddFeature, messageReceiver);
    }

    @Override
    protected void prepareFeatures() {
        super.prepareFeatures();

        features.add(new Feature(BidibLibrary.FEATURE_GEN_SPYMODE, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_WATCHDOG, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_DRIVE_ACK, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_SWITCH_ACK, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_POM_REPEAT, 1));

        features.add(new Feature(BidibLibrary.FEATURE_GEN_DRIVE_BUS, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_LOK_LOST_DETECT, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_NOTIFY_DRIVE_MANUAL, 1));

        features.add(new Feature(BidibLibrary.FEATURE_GEN_RCPLUS_AVAILABLE, 1));

        // remove FW udpate feature
        features.remove(new Feature(BidibLibrary.FEATURE_FW_UPDATE_MODE, 1));
    }

    @Override
    public String getSimulationPanelClass() {
        return SIMULATION_PANEL_CLASS;
    }

    /**
     * @return the subNodes
     */
    public Map getSubNodes() {
        return subNodes;
    }

    /**
     * Add a new subnode.
     * 
     * @param simulator
     *            the simulator
     */
    @Override
    public void addSubNode(SimulatorNode simulator) {
        String nodeAddress = simulator.getLocalAddress().trim();

        // we must store the node under the local address

        LOGGER.info("Add new subnode, address: {}, simulator: {}", nodeAddress, simulator);
        subNodes.put(nodeAddress, simulator);
    }

    @Override
    public void start() {
        LOGGER.info("Start the simulator for address: {}", getAddress());

        // AnnotationProcessor.process(this);

        // setup loco decoder CV
        mapLocoCV.put(1, Integer.valueOf(3));
        mapLocoCV.put(29, Integer.valueOf(0));
        mapLocoCV.put(28, Integer.valueOf(3)); // set the railcom config

        super.start();
    }

    @Override
    public void stop() {
        // AnnotationProcessor.unprocess(this);
        super.stop();
    }

    @Override
    protected byte[] prepareResponse(BidibCommand bidibMessage) {

        byte[] response = null;
        switch (ByteUtils.getInt(bidibMessage.getType())) {
            case BidibLibrary.MSG_CS_SET_STATE:
                response = processCsSetStateRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_POM:
                response = processCsPomRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_PROG:
                response = processCsProgRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_DRIVE:
                response = processCsDriveRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_ACCESSORY:
                response = processCsAccessoryRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_RCPLUS:
                response = processCsRcPlusRequest(bidibMessage);
                break;
            default:
                response = super.prepareResponse(bidibMessage);
                break;
        }
        return response;
    }

    protected byte[] processCsSetStateRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the CsSetState request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationSetStateMessage commandStationSetStateMessage = (CommandStationSetStateMessage) bidibMessage;
            CommandStationState state = commandStationSetStateMessage.getState();
            LOGGER.info("The requested command station state is: {}", state);

            switch (state) {
                case OFF:
                    commandStationState = CommandStationState.OFF;
                    break;
                case STOP:
                case SOFTSTOP:
                    commandStationState = CommandStationState.STOP;
                    break;
                case GO:
                case GO_IGN_WD:
                    commandStationState = CommandStationState.GO;
                    break;
                default:
                    break;
            }
            CommandStationStateResponse commandStationStateResponse =
                new CommandStationStateResponse(bidibMessage.getAddr(), getNextSendNum(), commandStationState.getType());
            response = commandStationStateResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationState response failed.", ex);
        }
        return response;
    }

    protected byte[] processCsPomRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the CsPom request: {}", bidibMessage);
        byte[] response = null;

        // prepare the POM acknowledge
        try {
            CommandStationPomMessage commandStationPomMessage = (CommandStationPomMessage) bidibMessage;
            org.bidib.jbidibc.core.AddressData addressData = commandStationPomMessage.getDecoderAddress();
            LOGGER.info("Received addressData: {}", addressData);
            CommandStationPomAcknowledgeResponse commandStationPomAckResponse =
                new CommandStationPomAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1);
            response = commandStationPomAckResponse.getContent();

            LOGGER.info("Publish the running response: {}", commandStationPomAckResponse);
            publishResponse(response);
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationPomAck response failed.", ex);
        }

        // prepare the MSG_BM_CV that contains the real data
        try {
            LOGGER.info("prepare the MSG_BM_CV that contains the real data.");
            CommandStationPomMessage commandStationPomMessage = (CommandStationPomMessage) bidibMessage;
            org.bidib.jbidibc.core.AddressData addressData = commandStationPomMessage.getDecoderAddress();
            int cvNumber = commandStationPomMessage.getCvNumber();
            byte cvValue = 12;
            FeedbackCvResponse feedbackCvResponse =
                new FeedbackCvResponse(bidibMessage.getAddr(), getNextSendNum(), addressData.getAddress(), cvNumber,
                    cvValue);
            response = feedbackCvResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationPomAck response failed.", ex);
        }

        return response;
    }

    protected byte[] processCsProgRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the CsProg request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationProgMessage commandStationProgMessage = (CommandStationProgMessage) bidibMessage;
            CommandStationPt opCode = commandStationProgMessage.getOpCode();
            int cvNumber = commandStationProgMessage.getCvNumber();
            int cvData = commandStationProgMessage.getCvData();
            LOGGER.info("Received opCode: {}, cvNumber: {}, cvData: {}", opCode, cvNumber, cvData);
            boolean sendTimeoutResponse = false;
            switch (opCode) {
                case BIDIB_CS_PROG_BREAK:
                    break;
                case BIDIB_CS_PROG_QUERY:
                    break;
                case BIDIB_CS_PROG_RDWR_BIT:
                    if ((cvData & 0x10) == 0x10) {
                        Integer storedValue = mapLocoCV.get(cvNumber + 1);
                        // write operation
                        if (storedValue != null) {
                            byte byteValue = ByteUtils.getLowByte(storedValue.intValue());
                            byteValue =
                                ByteUtils.setBit(byteValue, (cvData & 0x08) == 0x08 ? true : false, cvData & 0x07);
                            LOGGER.info("Changed CV value: {}", ByteUtils.byteToHex(byteValue));
                            mapLocoCV.put(cvNumber + 1, Integer.valueOf(byteValue));
                        }
                        else {
                            byte byteValue = 0;
                            byteValue =
                                ByteUtils.setBit(byteValue, (cvData & 0x08) == 0x08 ? true : false, cvData & 0x07);
                            LOGGER.info("Changed CV value: {}", ByteUtils.byteToHex(byteValue));
                            mapLocoCV.put(cvNumber + 1, Integer.valueOf(byteValue));
                        }
                    }
                    else {
                        // read operation
                        Integer storedValue = mapLocoCV.get(cvNumber + 1);
                        if (storedValue != null) {
                            byte byteValue = ByteUtils.getLowByte(storedValue.intValue());
                            boolean bitIsSetEqual =
                                ByteUtils.isBitSetEqual(byteValue, ByteUtils.getBit(cvData, 3), cvData & 0x07);
                            LOGGER.info("Verify bitIsSetEqual: {}, byteValue: {}", bitIsSetEqual, byteValue);
                            if (!bitIsSetEqual) {
                                LOGGER.warn("Send timeout response!");
                                sendTimeoutResponse = true;
                            }
                        }
                        else {
                            LOGGER.warn("The requested CV value is not stored, cvNumber: {}", cvNumber);
                            sendTimeoutResponse = true;
                        }
                    }
                    break;
                case BIDIB_CS_PROG_WR_BYTE:
                    mapLocoCV.put(cvNumber + 1, cvData);
                    break;
                default:
                    Integer storedValue = mapLocoCV.get(cvNumber + 1);
                    if (storedValue != null) {
                        cvData = ByteUtils.getLowByte(storedValue.intValue());
                    }
                    else {
                        LOGGER.warn("The requested CV value is not stored, cvNumber: {}", cvNumber);
                        sendTimeoutResponse = true;
                    }
                    break;
            }

            CommandStationProgStateResponse commandStationProgStateResponse =
                new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                    CommandStationProgState.PROG_RUNNING, 1, cvNumber, cvData);
            response = commandStationProgStateResponse.getContent();
            LOGGER.info("Publish the running response: {}", commandStationProgStateResponse);
            publishResponse(response);

            LOGGER.info("Sleep a second.");
            // sleep some time
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                LOGGER.warn("Sleep thread was interrupted.", e);
            }

            if (!sendTimeoutResponse) {
                LOGGER.info("Prepare the OKAY state response.");
                commandStationProgStateResponse =
                    new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                        CommandStationProgState.PROG_OKAY, 1, cvNumber, cvData);
            }
            else {
                LOGGER.info("Prepare the NO_ANSWER state response.");
                commandStationProgStateResponse =
                    new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                        CommandStationProgState.PROG_NO_ANSWER, 1, cvNumber, 0xFF);
            }
            response = commandStationProgStateResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationProgState response failed.", ex);
        }
        return response;
    }

    protected byte[] processCsDriveRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the CsDrive request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationDriveMessage commandStationDriveMessage = (CommandStationDriveMessage) bidibMessage;
            org.bidib.jbidibc.core.AddressData addressData = commandStationDriveMessage.getDecoderAddress();
            LOGGER.info("Received addressData: {}", addressData);
            CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
                new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1);
            response = commandStationDriveAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationDriveAck response failed.", ex);
        }
        return response;
    }

    protected byte[] processCsAccessoryRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the CsAccessory request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationAccessoryMessage commandStationAccessoryMessage =
                (CommandStationAccessoryMessage) bidibMessage;
            org.bidib.jbidibc.core.AddressData addressData = commandStationAccessoryMessage.getDecoderAddress();
            LOGGER.info("Received addressData: {}", addressData);
            CommandStationAccessoryAcknowledgeResponse commandStationAccessoryAckResponse =
                new CommandStationAccessoryAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1);
            response = commandStationAccessoryAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationAccessoryAck response failed.", ex);
        }
        return response;
    }

    protected byte[] processCsRcPlusRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the CsRcPlus request: {}", bidibMessage);
        byte[] response = null;

        // prepare the RCPLUS acknowledge
        try {
            CommandStationRcPlusMessage commandStationRcPlusMessage = (CommandStationRcPlusMessage) bidibMessage;

            CommandStationRcPlusAcknowledgeResponse acknowledgeResponse = null;
            switch (commandStationRcPlusMessage.getOpCode()) {
                case RC_GET_TID:
                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), tid);

                    response = acknowledgeResponse.getContent();
                    break;
                case RC_SET_TID:
                    TidData newTid = commandStationRcPlusMessage.getTid();
                    LOGGER.info("Set the new TID: {}", newTid);
                    tid = newTid;

                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), tid);

                    response = acknowledgeResponse.getContent();
                    break;
                case RC_PING_ONCE_P0:
                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(),
                            RcPlusPhase.P0, 0x00);

                    response = acknowledgeResponse.getContent();
                    break;
                case RC_PING_ONCE_P1:
                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(),
                            RcPlusPhase.P1, 0x00);

                    response = acknowledgeResponse.getContent();
                    break;
                default:
                    LOGGER.error("The RailCom+ acknowledge is not yet implemented for OpCode: {}",
                        commandStationRcPlusMessage.getOpCode());
                    break;
            }

        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationRcPlusAck response failed.", ex);
        }
        return response;
    }

    protected byte[] processNodeTabGetAllRequest(BidibCommand bidibMessage) {

        // reset the local addr
        localAddrIndex = 0;
        byte tabCount = (byte) subNodes.size();

        LOGGER.info("Return number of nodeTabs in current node: {}", tabCount);

        byte[] response = null;
        try {
            NodeTabCountResponse nodeTabCountResponse =
                new NodeTabCountResponse(bidibMessage.getAddr(), getNextSendNum(), tabCount);
            response = nodeTabCountResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create nodeTabCount response failed.", ex);
        }
        return response;
    }

    protected byte[] processNodeTabGetNextRequest(BidibCommand bidibMessage) {

        // 02.01.2014 14:50:11.961: send NodeTabGetAllMessage[num=4,type=11,data=[]] : FE 03 00 04 0B 93 FE
        // 02.01.2014 14:50:11.974: receive NodeTabCountResponse[[0],num=1,type=136,data=[2]] : 04 00 01 88 02
        // 02.01.2014 14:50:11.977: send NodeTabGetNextMessage[num=5,type=12,data=[]] : FE 03 00 05 0C D4 FE
        // 02.01.2014 14:50:11.990: receive NodeTabResponse[[0],num=2,type=137,data=[1, 0, 210, 0, 13, 104, 0, 0, 54]] :
        // 0C 00 02 89 01 00 D2 00 0D 68 00 00 36
        // 02.01.2014 14:50:11.994: send SysMagicMessage[num=6,type=1,data=[]] : FE 03 00 06 01 7C FE
        // 02.01.2014 14:50:12.006: receive SysMagicResponse[[0],num=0,type=129,data=[254, 175]] : 05 00 00 81 FE AF
        // 02.01.2014 14:50:12.008: send NodeTabGetNextMessage[num=7,type=12,data=[]] : FE 03 00 07 0C 45 FE
        // 02.01.2014 14:50:12.022: receive NodeTabResponse[[0],num=1,type=137,data=[1, 1, 5, 0, 13, 107, 0, 105, 234]]
        // : 0C 00 01 89 01 01 05 00 0D 6B 00 69 EA
        // 02.01.2014 14:50:12.031: send SysMagicMessage[num=0,type=1,data=[]] : FE 04 01 00 00 01 CE FE
        // 02.01.2014 14:50:12.038: receive SysMagicResponse[[1],num=0,type=129,data=[254, 175]] : 06 01 00 00 81 FE AF

        byte[] response = null;
        byte nodeTabVersion = ByteUtils.getLowByte(SimulatorRegistry.getInstance().getNextNodeTabVersion());
        byte localAddr = localAddrIndex;

        LOGGER.info("get the simulator with local address: {}", localAddr);

        String[] nodeAddresses = subNodes.keySet().toArray(new String[0]);

        byte addr = ByteUtils.getLowByte(Integer.parseInt(nodeAddresses[localAddr], 16));

        SimulatorNode simulator = subNodes.get(nodeAddresses[localAddr]);
        long uniqueId = simulator.getUniqueId();

        LOGGER.info("localAddr: {}, nodeAddr: {}, uniqueId: {}", localAddr, addr, uniqueId);

        try {
            NodeTabResponse nodeTabResponse =
                new NodeTabResponse(bidibMessage.getAddr(), getNextSendNum(), nodeTabVersion, addr, uniqueId);
            response = nodeTabResponse.getContent();
            LOGGER.info("Prepared nodeTab response: {}", ByteUtils.bytesToHex(response));
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create nodeTab response failed.", ex);
        }

        localAddrIndex++;

        return response;
    }

    protected void processResetRequest(BidibCommand bidibMessage) {
        LOGGER.info("Process the reset request, bidibMessage: {}", bidibMessage);
        resetSendNum();

        // notify the master that we're gone
        // EventBus.publish(new NodeLostEvent(nodeAddress[nodeAddress.length - 1]));
    }

    @EventSubscriber(eventClass = NodeLostEvent.class)
    public void nodeLostEvent(NodeLostEvent nodeLostEvent) {
        byte[] nodeAddr = nodeLostEvent.getNodeAddr();

        // check if the node is a subnode of this node
        if (!NodeUtils.isSubNode(nodeAddress, nodeAddr)) {
            return;
        }

        final byte nodeLocalAddress = nodeAddr[nodeAddr.length - 1];
        long uniqueId = nodeLostEvent.getUniqueId();
        LOGGER
            .info("The node lost event was requested, nodeLocalAddress: {}, uniqueId: {}", nodeLocalAddress, uniqueId);

        // publish event
        byte[] response = null;
        byte nodeTabVersion = ByteUtils.getLowByte(SimulatorRegistry.getInstance().getNextNodeTabVersion());
        try {
            byte addr = nodeLocalAddress;
            NodeLostResponse nodeLostResponse =
                new NodeLostResponse(nodeAddress, getNextSendNum(), nodeTabVersion, addr, uniqueId);

            response = nodeLostResponse.getContent();
            LOGGER.info("Prepared nodeLost response: {}", ByteUtils.bytesToHex(response));
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create nodeLost response failed.", ex);
        }

        LOGGER.info("Publish the current response: {}", response);
        publishResponse(response);
    }

    @EventSubscriber(eventClass = NodeAvailableEvent.class)
    public void nodeAvailableEvent(NodeAvailableEvent nodeAvailableEvent) {
        byte[] nodeAddr = nodeAvailableEvent.getNodeAddr();

        // check if the node is a subnode of this node
        if (!NodeUtils.isSubNode(nodeAddress, nodeAddr)) {
            return;
        }

        final byte nodeLocalAddress = nodeAddr[nodeAddr.length - 1];
        long uniqueId = nodeAvailableEvent.getUniqueId();
        LOGGER.info("The node available event was requested, nodeLocalAddress: {}, uniqueId: {}", nodeLocalAddress,
            uniqueId);

        // publish event
        byte[] response = null;
        byte nodeTabVersion = ByteUtils.getLowByte(SimulatorRegistry.getInstance().getNextNodeTabVersion());
        try {
            byte addr = nodeLocalAddress;
            NodeNewResponse nodeNewResponse =
                new NodeNewResponse(nodeAddress, getNextSendNum(), nodeTabVersion, addr, uniqueId);

            response = nodeNewResponse.getContent();
            LOGGER.info("Prepared nodeNew response: {}", ByteUtils.bytesToHex(response));
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create nodeNew response failed.", ex);
        }

        LOGGER.info("Publish the current response: {}", response);
        publishResponse(response);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy