Search in sources :

Example 1 with MqttConnection

use of net.solarnetwork.common.mqtt.MqttConnection in project solarnetwork-node by SolarNetwork.

the class FluxUploadService method setRequiredOperationalMode.

/**
 * Set an operational mode that must be active for a connection to SolarFlux
 * to be established.
 *
 * @param requiredOperationalMode
 *        the mode to require, or {@literal null} to enable by default
 */
public void setRequiredOperationalMode(String requiredOperationalMode) {
    if (requiredOperationalMode == this.requiredOperationalMode || (this.requiredOperationalMode != null && this.requiredOperationalMode.equals(requiredOperationalMode))) {
        return;
    }
    this.requiredOperationalMode = requiredOperationalMode;
    Runnable task = new Runnable() {

        @Override
        public void run() {
            MqttConnection client = connection();
            if (requiredOperationalMode == null || requiredOperationalMode.isEmpty()) {
                if (client == null) {
                    // start up client now
                    startup();
                }
            } else if (client != null) {
                if (opModesService == null || !opModesService.isOperationalModeActive(requiredOperationalMode)) {
                    // shut down client, op mode no longer active
                    shutdown();
                }
            }
        }
    };
    Executor e = this.executor;
    if (e != null) {
        e.execute(task);
    } else {
        task.run();
    }
}
Also used : MqttConnection(net.solarnetwork.common.mqtt.MqttConnection) ReconfigurableMqttConnection(net.solarnetwork.common.mqtt.ReconfigurableMqttConnection) Executor(java.util.concurrent.Executor)

Example 2 with MqttConnection

use of net.solarnetwork.common.mqtt.MqttConnection in project solarnetwork-node by SolarNetwork.

the class FluxUploadService method configurationChanged.

@Override
public synchronized void configurationChanged(Map<String, Object> properties) {
    getMqttConfig().setUid("SolarFluxUpload-" + getMqttConfig().getServerUriValue());
    MqttConnection conn = connection();
    if (conn instanceof ReconfigurableMqttConnection) {
        ((ReconfigurableMqttConnection) conn).reconfigure();
    }
    FluxFilterConfig[] filters = getFilters();
    if (filters != null) {
        for (FluxFilterConfig f : filters) {
            f.configurationChanged(properties);
        }
    }
}
Also used : ReconfigurableMqttConnection(net.solarnetwork.common.mqtt.ReconfigurableMqttConnection) MqttConnection(net.solarnetwork.common.mqtt.MqttConnection) ReconfigurableMqttConnection(net.solarnetwork.common.mqtt.ReconfigurableMqttConnection)

Example 3 with MqttConnection

use of net.solarnetwork.common.mqtt.MqttConnection in project solarnetwork-node by SolarNetwork.

the class MqttUploadServiceTests method createMqttClient.

private MqttConnection createMqttClient(String clientId, MqttMessageHandler messageHandler) {
    BasicMqttConnectionConfig config = new BasicMqttConnectionConfig(service.getMqttConfig());
    config.setClientId(clientId);
    MqttConnection conn = connectionFactory.createConnection(config);
    conn.setMessageHandler(messageHandler);
    try {
        conn.open().get(MQTT_TIMEOUT, TimeUnit.SECONDS);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return conn;
}
Also used : MqttConnection(net.solarnetwork.common.mqtt.MqttConnection) BasicMqttConnectionConfig(net.solarnetwork.common.mqtt.BasicMqttConnectionConfig) IOException(java.io.IOException) DuplicateKeyException(org.springframework.dao.DuplicateKeyException)

Example 4 with MqttConnection

use of net.solarnetwork.common.mqtt.MqttConnection in project solarnetwork-node by SolarNetwork.

the class MqttUploadServiceTests method processInstruction_noHandler.

@Test
public void processInstruction_noHandler() throws Exception {
    // GIVEN
    TestingMqttMessageHandler messageHandler = new TestingMqttMessageHandler();
    MqttConnection solarNetClient = createMqttClient("solarnet", messageHandler);
    // parse instructions
    String testInstructions = getStringResource("instructions-01.json");
    List<Instruction> instructions = parseInstructions(testInstructions);
    assert instructions.size() == 1;
    final Instruction inputInstruction = instructions.get(0);
    // persist Executing state
    Capture<Instruction> storeInstructionCaptor = Capture.newInstance();
    reactorService.storeInstruction(capture(storeInstructionCaptor));
    // execute single instruction
    Capture<Instruction> execInstructionCaptor = Capture.newInstance();
    expect(instructionExecutionService.executeInstruction(capture(execInstructionCaptor))).andReturn(null);
    // save result back to DB
    Capture<Instruction> storeReceivedInstructionCaptor = Capture.newInstance();
    reactorService.storeInstruction(capture(storeReceivedInstructionCaptor));
    // save ack result back to DB
    Capture<Instruction> storeReceivedAckInstructionCaptor = Capture.newInstance();
    reactorService.storeInstruction(capture(storeReceivedAckInstructionCaptor));
    replayAll();
    // WHEN
    // allow time for subscription to take
    Thread.sleep(1000);
    String instrTopic = instructionTopic(nodeId);
    solarNetClient.publish(new BasicMqttMessage(instrTopic, false, MqttQos.AtLeastOnce, testInstructions.getBytes("UTF-8"))).get(MQTT_TIMEOUT, TimeUnit.SECONDS);
    // allow time for messages to process
    Thread.sleep(2000);
    // shut down server
    stopMqttServer();
    // THEN
    // should have stored Executing status
    Instruction storeInstruction = storeInstructionCaptor.getValue();
    assertThat("Store instruction ID", storeInstruction.getId(), equalTo(inputInstruction.getId()));
    assertThat("Store instruction instructorId", storeInstruction.getInstructorId(), equalTo(TEST_SOLARIN_BASE_URL));
    assertThat("Store instruction state", storeInstruction.getInstructionState(), equalTo(InstructionState.Executing));
    assertThat("Store instruction no ack", storeInstruction.getStatus().getAcknowledgedInstructionState(), nullValue());
    // should have executed Instruction with persisted local ID
    Instruction execInstruction = execInstructionCaptor.getValue();
    assertThat("Exec instruction has ID", execInstruction.getId(), equalTo(inputInstruction.getId()));
    assertThat("Exec instruction instructorId", execInstruction.getInstructorId(), equalTo(TEST_SOLARIN_BASE_URL));
    // should have stored Received status
    Instruction receivedInstruction = storeReceivedInstructionCaptor.getValue();
    assertThat("Received instruction ID", receivedInstruction.getId(), equalTo(inputInstruction.getId()));
    assertThat("Received instruction instructorId", receivedInstruction.getInstructorId(), equalTo(TEST_SOLARIN_BASE_URL));
    assertThat("Received instruction state", receivedInstruction.getInstructionState(), equalTo(InstructionState.Received));
    assertThat("Received instruction no ack", receivedInstruction.getStatus().getAcknowledgedInstructionState(), nullValue());
    // should have stored Received status
    Instruction receivedAckInstruction = storeReceivedAckInstructionCaptor.getValue();
    assertThat("Received ack instruction has persisted local ID", receivedAckInstruction.getId(), equalTo(inputInstruction.getId()));
    assertThat("Received ack instruction instructorId", receivedAckInstruction.getInstructorId(), equalTo(TEST_SOLARIN_BASE_URL));
    assertThat("Received ack instruction state", receivedAckInstruction.getInstructionState(), equalTo(InstructionState.Received));
    assertThat("Received ack instruction ack state", receivedAckInstruction.getStatus().getAcknowledgedInstructionState(), equalTo(InstructionState.Received));
    // should have published acknowledgement on datum topic
    TestingInterceptHandler session = getTestingInterceptHandler();
    assertThat("Published instruction and acks", session.publishMessages, hasSize(3));
    InterceptPublishMessage pubMsg = session.publishMessages.get(0);
    assertThat("Instruction client ID", pubMsg.getClientID(), equalTo("solarnet"));
    assertThat("Instruction topic", pubMsg.getTopicName(), equalTo(instructionTopic(nodeId)));
    pubMsg = session.publishMessages.get(1);
    assertThat("Instruction Executing ack client ID", pubMsg.getClientID(), equalTo(nodeId.toString()));
    assertThat("Instruction Executing topic", pubMsg.getTopicName(), equalTo(datumTopic(nodeId)));
    JSONAssert.assertEquals("Instruction Executing ack payload", "{\"instructionId\":" + inputInstruction.getId() + ",\"state\":\"Executing\"}", session.getPublishPayloadStringAtIndex(1), false);
    pubMsg = session.publishMessages.get(2);
    assertThat("Instruction Received ack client ID", pubMsg.getClientID(), equalTo(nodeId.toString()));
    assertThat("Instruction Received ack topic", pubMsg.getTopicName(), equalTo(datumTopic(nodeId)));
    JSONAssert.assertEquals("Instruction Completed ack payload", "{\"instructionId\":" + inputInstruction.getId() + ",\"state\":\"Received\"}", session.getPublishPayloadStringAtIndex(2), false);
}
Also used : InterceptPublishMessage(io.moquette.interception.messages.InterceptPublishMessage) MqttConnection(net.solarnetwork.common.mqtt.MqttConnection) BasicMqttMessage(net.solarnetwork.common.mqtt.BasicMqttMessage) TestingInterceptHandler(net.solarnetwork.test.mqtt.TestingInterceptHandler) Instruction(net.solarnetwork.node.reactor.Instruction) BasicInstruction(net.solarnetwork.node.reactor.BasicInstruction) Test(org.junit.Test)

Example 5 with MqttConnection

use of net.solarnetwork.common.mqtt.MqttConnection in project solarnetwork-node by SolarNetwork.

the class MqttUploadService method handleMqttMessage.

private void handleMqttMessage(MqttMessage message) {
    final String topic = message.getTopic();
    // look for and process instructions from message body, as JSON array
    final ReactorService reactor = OptionalService.service(reactorServiceOpt);
    if (reactor == null) {
        return;
    }
    final InstructionExecutionService executor = OptionalService.service(instructionExecutionServiceOpt);
    final String instructorId = identityService.getSolarInBaseUrl();
    try {
        JsonNode root = objectMapper.readTree(message.getPayload());
        JsonNode instrArray = root.path("instructions");
        if (instrArray == null || !instrArray.isArray()) {
            return;
        }
        final MqttConnection conn = connection();
        final Long nodeId = identityService.getNodeId();
        for (JsonNode instrNode : instrArray) {
            try {
                net.solarnetwork.domain.Instruction commonInstr = objectMapper.treeToValue(instrNode, net.solarnetwork.domain.Instruction.class);
                if (commonInstr == null) {
                    continue;
                }
                Instruction instr = BasicInstruction.from(commonInstr, instructorId);
                if (log.isInfoEnabled()) {
                    log.info("Instruction {} {} received with parameters: {}", instr.getId(), instr.getTopic(), instr.getParameterMap());
                }
                getMqttStats().incrementAndGet(SolarInCountStat.InstructionsReceived);
                InstructionStatus status = null;
                if (executor != null) {
                    // save with Executing state immediately, to prevent reactor job from picking up
                    status = new BasicInstructionStatus(instr.getId(), InstructionState.Executing, Instant.now());
                    instr = new BasicInstruction(instr, status);
                    reactor.storeInstruction(instr);
                    // execute immediately with our executor; pass Executing status back first
                    publishInstructionAck(conn, nodeId, instr);
                    status = executor.executeInstruction(instr);
                    if (status == null) {
                        log.info("No handler available for instruction {} {}: deferring to Received state", instr.getId(), instr.getTopic());
                        // instruction not handled: change instruction status to Received
                        status = new BasicInstructionStatus(instr.getId(), InstructionState.Received, Instant.now());
                    }
                } else {
                    // execution didn't happen, so pass to deferred executor
                    status = reactor.processInstruction(instr);
                }
                if (status == null) {
                    // deferred executor didn't handle, so decline
                    status = new BasicInstructionStatus(instr.getId(), InstructionState.Declined, Instant.now());
                }
                instr = new BasicInstruction(instr, status);
                reactor.storeInstruction(instr);
                postInstructionAck(reactor, conn, nodeId, instr);
            } catch (Exception e) {
                log.warn("Unable to accept instruction JSON [{}]: {}", instrNode, e.toString());
            }
        }
    } catch (RuntimeException | IOException e) {
        log.error("Error handling MQTT message on topic {}", topic, e);
    }
}
Also used : BasicInstructionStatus(net.solarnetwork.node.reactor.BasicInstructionStatus) ReactorService(net.solarnetwork.node.reactor.ReactorService) InstructionExecutionService(net.solarnetwork.node.reactor.InstructionExecutionService) MqttConnection(net.solarnetwork.common.mqtt.MqttConnection) InstructionStatus(net.solarnetwork.node.reactor.InstructionStatus) BasicInstructionStatus(net.solarnetwork.node.reactor.BasicInstructionStatus) JsonNode(com.fasterxml.jackson.databind.JsonNode) IOException(java.io.IOException) Instruction(net.solarnetwork.node.reactor.Instruction) BasicInstruction(net.solarnetwork.node.reactor.BasicInstruction) URISyntaxException(java.net.URISyntaxException) TimeoutException(java.util.concurrent.TimeoutException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) BasicInstruction(net.solarnetwork.node.reactor.BasicInstruction)

Aggregations

MqttConnection (net.solarnetwork.common.mqtt.MqttConnection)15 BasicMqttMessage (net.solarnetwork.common.mqtt.BasicMqttMessage)7 IOException (java.io.IOException)5 TimeoutException (java.util.concurrent.TimeoutException)5 JsonNode (com.fasterxml.jackson.databind.JsonNode)4 ExecutionException (java.util.concurrent.ExecutionException)4 ReconfigurableMqttConnection (net.solarnetwork.common.mqtt.ReconfigurableMqttConnection)4 BasicInstruction (net.solarnetwork.node.reactor.BasicInstruction)4 Instruction (net.solarnetwork.node.reactor.Instruction)4 InterceptPublishMessage (io.moquette.interception.messages.InterceptPublishMessage)3 TestingInterceptHandler (net.solarnetwork.test.mqtt.TestingInterceptHandler)3 Test (org.junit.Test)3 MqttStats (net.solarnetwork.common.mqtt.MqttStats)2 BasicInstructionStatus (net.solarnetwork.node.reactor.BasicInstructionStatus)2 InstructionStatus (net.solarnetwork.node.reactor.InstructionStatus)2 DuplicateKeyException (org.springframework.dao.DuplicateKeyException)2 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)1 ArrayNode (com.fasterxml.jackson.databind.node.ArrayNode)1 JsonNodeCreator (com.fasterxml.jackson.databind.node.JsonNodeCreator)1 JaxbAnnotationModule (com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule)1