Skip to content

Commit 9106a0a

Browse files
authored
Merge pull request #3447 from High-Voltage-Engineering/dh-scan-in-operator
Add 'in' operator for scan server scripts
2 parents f136195 + 907d6ec commit 9106a0a

File tree

5 files changed

+165
-4
lines changed

5 files changed

+165
-4
lines changed

app/scan/model/src/main/java/org/csstudio/scan/command/Comparison.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ public enum Comparison
4343
INCREASE_BY("to increase by"),
4444

4545
/** Value has decreased by some amount */
46-
DECREASE_BY("to decrease by");
46+
DECREASE_BY("to decrease by"),
47+
48+
/** (Discrete) value has to be included in some list (only for TextValueConditions) */
49+
IN("in"),
50+
51+
/** (Discrete) value has to NOT be included in some list (only for TextValueConditions) */
52+
NOT_IN("not in");
4753

4854
final private String label;
4955

app/scan/model/src/main/java/org/csstudio/scan/command/WaitCommand.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ public void addXMLElements(final Document dom, final Element command_element)
195195
public void readXML(final Element element) throws Exception
196196
{
197197
setDeviceName(XMLUtil.getChildString(element, ScanCommandProperty.TAG_DEVICE).orElse(""));
198-
setDesiredValue(StringOrDouble.parse(XMLUtil.getChildString(element, ScanCommandProperty.TAG_VALUE).orElse("0")));
199198
try
200199
{
201200
setComparison(Comparison.valueOf(XMLUtil.getChildString(element, "comparison").orElse(Comparison.EQUALS.toString())));
@@ -204,6 +203,14 @@ public void readXML(final Element element) throws Exception
204203
{
205204
setComparison(Comparison.EQUALS);
206205
}
206+
if (getComparison() == Comparison.IN || getComparison() == Comparison.NOT_IN) {
207+
// don't remove string quotes!
208+
// must be parsed later to an array of values using Strings.parseStringList!
209+
setDesiredValue(XMLUtil.getChildString(element, ScanCommandProperty.TAG_VALUE).orElse("0"));
210+
}
211+
else {
212+
setDesiredValue(StringOrDouble.parse(XMLUtil.getChildString(element, ScanCommandProperty.TAG_VALUE).orElse("0")));
213+
}
207214
setTolerance(XMLUtil.getChildDouble(element, ScanCommandProperty.TAG_TOLERANCE).orElse(0.1));
208215
setTimeout(XMLUtil.getChildDouble(element, ScanCommandProperty.TAG_TIMEOUT).orElse(0.0));
209216
super.readXML(element);

core/util/src/main/java/org/phoebus/util/text/Strings.java

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.phoebus.util.text;
22

3+
import java.util.ArrayList;
4+
import java.util.List;
35
import java.util.Objects;
46

57
public class Strings {
@@ -17,4 +19,106 @@ public static boolean isNullOrEmpty(String str) {
1719
return false;
1820
}
1921
}
22+
23+
public static List<String> parseStringList(String text) {
24+
final List<String> result = new ArrayList<>();
25+
if (text == null || text.isEmpty()) {
26+
return result;
27+
}
28+
29+
final StringBuilder current = new StringBuilder();
30+
Character quote = null;
31+
32+
for (int i = 0; i < text.length(); i++) {
33+
final char c = text.charAt(i);
34+
35+
if (quote == null) {
36+
if (Character.isWhitespace(c) && current.isEmpty()) {
37+
// skip non-quoted whitespace character at the start of unquoted string
38+
}
39+
else if (c == '\"' || c == '\'') {
40+
// start quoted string, assert that current StringBuilder is empty
41+
if (!current.isEmpty()) {
42+
// string contained non-whitespace, characters so it was of the form
43+
// "input 'start quote', next element"
44+
// which is invalid
45+
throw new RuntimeException("Unexpected quote in non-quoted string: " + current + c);
46+
}
47+
quote = c;
48+
}
49+
else if (c == ',') {
50+
// end unquoted string, add trimmed value and reset
51+
result.add(current.toString().trim());
52+
current.setLength(0);
53+
}
54+
else {
55+
// other character, just add to the current value
56+
current.append(c);
57+
}
58+
}
59+
else {
60+
if (c == quote) {
61+
// end quoted string, expect whitespace then comma
62+
String elt = current.toString();
63+
result.add(elt);
64+
current.setLength(0);
65+
66+
// also reset quote
67+
quote = null;
68+
69+
while (true) {
70+
i++;
71+
if (i >= text.length()) {
72+
// end of string, no more elements after this
73+
return result;
74+
}
75+
final char k = text.charAt(i);
76+
if (k == ',') {
77+
// start next element
78+
break;
79+
}
80+
if (Character.isWhitespace(k)) {
81+
// skip whitespace character in search for comma
82+
continue;
83+
}
84+
// non-whitespace, non-comma character, BAD!
85+
throw new RuntimeException(
86+
"Expected whitespace after string list element " + elt + " got " + k
87+
);
88+
}
89+
}
90+
else if (c == '\\') {
91+
// escaped quote?
92+
// we might want to support more escape sequences
93+
if (i + 1 < text.length() && text.charAt(i + i) == quote) {
94+
current.append(quote);
95+
i++;
96+
}
97+
else {
98+
// just a backslash...
99+
current.append(c);
100+
}
101+
}
102+
else {
103+
// other character, don't do anything special
104+
current.append(c);
105+
}
106+
}
107+
}
108+
109+
// last element may still be in here
110+
if (!current.isEmpty()) {
111+
if (quote != null) {
112+
throw new RuntimeException(
113+
"Unexpected end of string while scanning quoted string " + quote + current
114+
);
115+
}
116+
else {
117+
// non-quoted, just add trimmed value
118+
result.add(current.toString().trim());
119+
}
120+
}
121+
122+
return result;
123+
}
20124
}

core/util/src/test/java/org/phoebus/util/text/StringsTest.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818

1919
import org.junit.jupiter.api.Test;
2020

21-
import static org.junit.jupiter.api.Assertions.assertFalse;
22-
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
import java.util.List;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
2324

2425
/**
2526
* Unit test for {@link Strings}.
@@ -36,4 +37,35 @@ public void testIsNullOrEmpty() {
3637
assertFalse(Strings.isNullOrEmpty("a"));
3738
}
3839

40+
@Test
41+
void testParseStringList() {
42+
assertEquals(
43+
List.of("A", "B", "C"),
44+
Strings.parseStringList("A, B, C")
45+
);
46+
assertEquals(
47+
List.of("An apple", "an orange", "banana"),
48+
Strings.parseStringList("An apple, an orange, banana")
49+
);
50+
51+
assertEquals(
52+
List.of("An apple", "an orange", "banana"),
53+
Strings.parseStringList("'An apple', an orange, banana")
54+
);
55+
56+
assertEquals(
57+
List.of("INVALID", "ERROR"),
58+
Strings.parseStringList("\"INVALID\", \"ERROR\"")
59+
);
60+
61+
assertEquals(
62+
List.of(" item with leading and trailing spaces that are not removed "),
63+
Strings.parseStringList("' item with leading and trailing spaces that are not removed '")
64+
);
65+
66+
assertEquals(
67+
List.of("Hello, Dolly", "Goodbye, cruel world"),
68+
Strings.parseStringList("\"Hello, Dolly\", \"Goodbye, cruel world\"")
69+
);
70+
}
3971
}

services/scan-server/src/main/java/org/csstudio/scan/server/condition/TextValueCondition.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
package org.csstudio.scan.server.condition;
1717

1818
import java.time.Duration;
19+
import java.util.Arrays;
1920
import java.util.concurrent.TimeoutException;
2021

2122
import org.csstudio.scan.command.Comparison;
2223
import org.csstudio.scan.server.device.Device;
2324
import org.csstudio.scan.server.device.DeviceListener;
2425

2526
import org.phoebus.core.vtypes.VTypeHelper;
27+
import org.phoebus.util.text.Strings;
2628

2729
/** Condition that waits for a Device to reach a certain string value.
2830
*
@@ -119,6 +121,12 @@ public void await() throws TimeoutException, Exception
119121
}
120122
}
121123

124+
private boolean containsValue(String value) {
125+
return Strings.parseStringList(desired_value).stream().anyMatch(
126+
v -> v.equals(value) || v.equals(value.strip()) // strip input value just to be sure
127+
);
128+
}
129+
122130
/** Determine if the condition is currently met
123131
* @return <code>true</code> if condition is met
124132
* @throws Exception on error reading from the device
@@ -140,6 +148,10 @@ public boolean isConditionMet() throws Exception
140148
return value.compareTo(desired_value) <= 0;
141149
case BELOW:
142150
return value.compareTo(desired_value) < 0;
151+
case IN:
152+
return containsValue(value);
153+
case NOT_IN:
154+
return !containsValue(value);
143155
default:
144156
throw new Error("Condition not implemented for strings: " + comparison);
145157
}

0 commit comments

Comments
 (0)