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();
}
}
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);
}
}
}
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;
}
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);
}
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);
}
}
Aggregations