Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/alarm/examples/infopv.db
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Example for "Info PV"
# used with automated action set to "infopv:NameOfPV"
#
# softIoc -s -m N=NameOfPV -d infopv.db
#
# With Channel Access, use $(N).VAL$ to access the full text.

record(lsi, "$(N)")
{
field(SIZV, 1000)
field(INP, {const:""})
field(PINI, "YES")
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ automated_email_sender=Alarm Notifier <alarm_server@example.org>

# Comma-separated list of automated actions on which to follow up
# Options include mailto:, cmd:
automated_action_followup=mailto:, cmd:
automated_action_followup=mailto:, cmd:, infopv:

# Optional heartbeat PV
# When defined, alarm server will set it to 1 every heartbeat_secs
Expand Down
24 changes: 24 additions & 0 deletions app/alarm/ui/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,30 @@ Sends email with alarm detail to list of recipients.
The email server is configured in the alarm preferences.


``infopv:SomePV``:
Writes the alarm detail to a PV.

The PV needs to hold a string, for example
`mqtt://alarm/message<VString>` for an MQTT topic
or
`ca://NameOfPV.VAL$`
for Channel Access where the PV refers to a string record::

# Example for "Info PV"
# used with automated action set to "infopv:NameOfPV"
#
# softIoc -s -m N=NameOfPV -d infopv.db

record(lsi, "$(N)")
{
field(SIZV, 1000)
field(INP, {const:""})
field(PINI, "YES")
}

With Channel Access, since the text usually exceeds 40 characters, use ``infopv:SomePV.VAL$``.


``cmd:some_command arg1 arg2``:
Invokes command with list of space-separated arguments.
The special argument "*" will be replaced with a list of alarm PVs and their alarm severity.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018-2023 Oak Ridge National Laboratory.
* Copyright (c) 2018-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -49,8 +49,16 @@
@SuppressWarnings("nls")
public class TitleDetailDelayTable extends BorderPane
{
private enum Option_d {
mailto, cmd, sevrpv
private enum Option_d
{
// Send email with alarm info
mailto,
// Execute external command
cmd,
// Update PV with severity
sevrpv,
// Update PV with alarm info text
infopv
};

private final ObservableList<TitleDetailDelay> items = FXCollections.observableArrayList();
Expand Down
107 changes: 107 additions & 0 deletions core/pv-mqtt/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
MQTT PV Support
===============

MQTT is a broker-based protocol used in the Internet-of-Things (IoT) ecosystem.
See https://mqtt.org for details.
The `mqtt:...` PV support allows CS-Studio tools to read and write PVs via MQTT.


Example Broker Setup and first steps
------------------------------------

This example uses Eclipse Mosquitto on Linux.
See https://mqtt.org for links to several other MQTT brokers and clients.

Download: Get source release `mosquitto-....tar.gz` from https://mosquitto.org/download

Unpack: `tar vzxf mosquitto-....tar.gz`

Build: `make WITH_CJSON=no`

Install:

```
export LD_LIBRARY_PATH=`pwd`/lib
# Optionally, add the following to a `bin` folder that's on your $PATH
# src/mosquitto, client/mosquitto_sub, client/mosquitto_pub
```

Run broker:

```
# Allow remote access through firewall.
# Depending on Linux release, similar to this
sudo firewall-cmd --add-port=1883/tcp

# Create configuration file that allows remote access
echo "listener 1883" >> mosquitto.conf
echo "allow_anonymous true" >> mosquitto.conf

# Start broker with that configuration file
src/mosquitto -c mosquitto.conf
mosquitto version ... starting
Config loaded from mosquitto.conf.
...
Opening ipv4 listen socket on port 1883.
```

See https://mosquitto.org/documentation/authentication-methods
for a more secure configuration.

Subscribe to value updates: `client/mosquitto_sub -t sensors/temperature -q 1 `

Publish a value: `client/mosquitto_pub -t sensors/temperature -q 1 -m 42`

By default, the broker will not persist messages.
The subscribe command shown above will receive all newly
published messages. If you close the `mosquitto_sub` and then restart it,
it will show nothing until a new value is published.

To persist data on the broker, each client that publishes or subscribes
needs to connect with a unique client ID and an option to _not_ 'clean' the session,
using options like `--id 8765 --disable-clean-session` for the `.._sub` and `.._pub`
commands shown above.


MQTT PV Configuration
---------------------

By default, the MQTT PV will look for a broker on localhost
and the default MQTT broker port 1883.

To change this, add a variant of the following to your Phoebus settings:

```
# MQTT Broker
# All "mqtt://some/tag" PVs will use this broker
#org.phoebus.pv.mqtt/mqtt_broker=tcp://localhost:1883
org.phoebus.pv.mqtt/mqtt_broker=tcp://my_host.site.org:1883
```

The MQTT PV will create a unique internal ID to read persisted messages,
allowing the PV to start up with the last known value of an MQTT topic
without need to wait for the next update.


MQTT PV Syntax
--------------

To interface with the example MQTT tag shown above,
use the PV `mqtt://sensors/temperature`.

The general format is `mqtt://` followed by the MQTT topic,
for example `sensors/temperature`,
and an optional `<VType>`.

MQTT treats all tag data as text. By default, an MQTT PV expects
the text to contain a number, but the optional `<VType>` will
instruct the PV to parse the text in other ways.

| `VType` | PV Value Parser |
| ---------------- | ---------------------------------------------------------------------------- |
| `<VDouble>` | This is the default, expecting the topic to parse as a floating point number |
| `<VString>` | PV reads text as string |
| `<VLong>` | Parse as long integer |
| `<VDoubleArray>` | Parse as array of comma-separated floating point numbers |
| `<VStringArray>` | Parse text as array of comma-separated strings |

5 changes: 4 additions & 1 deletion services/alarm-server/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
<classpathentry combineaccessrules="false" kind="src" path="/core-util"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-pv"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-formula"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-vtype"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-pv-ca"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-pv-pva"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-pv-mqtt"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-vtype"/>
<classpathentry combineaccessrules="false" kind="src" path="/core-email"/>
<classpathentry combineaccessrules="false" kind="src" path="/app-alarm-model"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018-2022 Oak Ridge National Laboratory.
* Copyright (c) 2018-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand All @@ -15,7 +15,6 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import org.apache.kafka.clients.consumer.Consumer;
Expand All @@ -34,6 +33,7 @@
import org.phoebus.applications.alarm.model.SeverityLevel;
import org.phoebus.applications.alarm.model.json.JsonModelReader;
import org.phoebus.applications.alarm.model.json.JsonModelWriter;
import org.phoebus.applications.alarm.server.actions.InfoPVActionExecutor;

/** Server's model of the alarm configuration
*
Expand Down Expand Up @@ -120,6 +120,7 @@ public void start()
{
thread.start();
SeverityPVHandler.initialize();
InfoPVActionExecutor.initialize();

// Alarm server startup message
sendAnnunciatorMessage(root.getPathName(), SeverityLevel.OK, "* Alarm server started. Everything is going to be all right.");
Expand Down Expand Up @@ -603,6 +604,7 @@ private void clearActionsAndStopPVs(final AlarmTreeItem<?> node)
public void shutdown()
{
SeverityPVHandler.stop();
InfoPVActionExecutor.stop();
running = false;
consumer.wakeup();
try
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018-2019 Oak Ridge National Laboratory.
* Copyright (c) 2018-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -27,6 +27,9 @@
* <p>"mailto:user@site.org,another@else.com"<br>
* Sends email with alarm detail to list of recipients.
*
* <p>"infopv:ca://demo"<br>
* Writes alarm detail to string PV.
*
* <p>"cmd:some_command arg1 arg2"<br>
* Invokes command with list of space-separated arguments.
* The special argument "*" will be replaced with a list of alarm PVs and their alarm severity.
Expand All @@ -44,18 +47,21 @@ public void accept(final AlarmTreeItem<?> item, final TitleDetailDelay action)
// Perform the automated action in background thread
JobManager.schedule("Automated Action", monitor ->
{
if (action.detail.startsWith("mailto:"))
{
if (AlarmLogic.getDisableNotify() == true)
{
return;
}
EmailActionExecutor.sendEmail(item, action.detail.substring(7).split(" *, *"));
}
final boolean mailto = action.detail.startsWith("mailto:");
final boolean infopv = action.detail.startsWith("infopv:");
if (mailto || infopv)
{ // Are notifications disabled?
if (AlarmLogic.getDisableNotify())
return;
if (mailto)
EmailActionExecutor.sendEmail(item, action.detail.substring(7).split(" *, *"));
else
InfoPVActionExecutor.writeInfo(item, action.detail.substring(7).trim());
}
else if (action.detail.startsWith("cmd:"))
CommandActionExecutor.run(item, action.detail.substring(4));
else
logger.log(Level.WARNING, "Automated action " + action + " lacks 'mailto:' or 'cmd:' in detail");
logger.log(Level.WARNING, "Automated action " + action + " lacks 'mailto:', 'infopv:' or 'cmd:' in detail");
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018-2020 Oak Ridge National Laboratory.
* Copyright (c) 2018-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -54,7 +54,11 @@ static void sendEmail(final AlarmTreeItem<?> item, final String[] addresses)
}
}

private static String createTitle(final AlarmTreeItem<?> item)
/** Create title for email, also used by Info PV
* @param item Item for which to create title
* @return Title
*/
static String createTitle(final AlarmTreeItem<?> item)
{
final StringBuilder buf = new StringBuilder();

Expand All @@ -77,7 +81,11 @@ private static String createTitle(final AlarmTreeItem<?> item)
return buf.toString();
}

private static String createBody(final AlarmTreeItem<?> item)
/** Create info body for email, also used by Info PV
* @param item Item for which to create info
* @return Info text
*/
static String createBody(final AlarmTreeItem<?> item)
{
final StringBuilder buf = new StringBuilder();

Expand Down
Loading