Skip to content

Commit db62821

Browse files
authored
Merge pull request #2426 from endless03/master
Add a "PVFactory" that supports Tango's protocol
2 parents ee52204 + 009d575 commit db62821

File tree

11 files changed

+708
-1
lines changed

11 files changed

+708
-1
lines changed

core/pv/doc/index.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,16 @@ Examples ::
151151

152152
sys://timeOffset(12 hours)
153153
sys://timeOffset(1hour, time, 1)
154+
155+
156+
Tango
157+
------
158+
Tango is different from EPICS, the smallest unit is Device, which includes the commands, states, and attributes.
159+
The command and the attribute has been implemented, add prefix to PV Name in editing interface to distinguish, and the command usually has a return value, so need to use *Text Entry* or a combination of *Action button* and *Text Update* components to achieve it.
160+
Currently, all types of scalars are supported, but SPECTRUM and IMAGE are not yet supported.
161+
162+
Examples ::
163+
164+
tga://device/attribute
165+
tgc://device/command
166+

core/pv/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,11 @@
7171
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
7272
<version>1.2.2</version>
7373
</dependency>
74+
<dependency>
75+
<groupId>org.tango-controls</groupId>
76+
<artifactId>JTango</artifactId>
77+
<version>9.7.0</version>
78+
<type>pom</type>
79+
</dependency>
7480
</dependencies>
7581
</project>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package org.phoebus.pv.tga;
2+
3+
import fr.esrf.Tango.DevFailed;
4+
import fr.esrf.Tango.EventProperties;
5+
import fr.esrf.TangoApi.AttributeInfoEx;
6+
import fr.esrf.TangoApi.AttributeProxy;
7+
import fr.esrf.TangoApi.DeviceAttribute;
8+
import org.epics.vtype.*;
9+
import org.phoebus.pv.PV;
10+
import org.tango.attribute.AttributeTangoType;
11+
import org.tango.server.events.EventType;
12+
13+
import java.util.concurrent.ConcurrentHashMap;
14+
import java.util.logging.Level;
15+
16+
;
17+
18+
public class TangoAttrContext {
19+
private static TangoAttrContext instance;
20+
private final ConcurrentHashMap<String, AttributeProxy> attributeProxys;
21+
private final ConcurrentHashMap<String, Integer> events;
22+
private final ConcurrentHashMap<String, AttributeTangoType> types;
23+
24+
25+
26+
private TangoAttrContext() {
27+
events = new ConcurrentHashMap<>();
28+
attributeProxys = new ConcurrentHashMap<>();
29+
types = new ConcurrentHashMap<>();
30+
}
31+
32+
public static synchronized TangoAttrContext getInstance() throws Exception {
33+
if (instance == null)
34+
instance = new TangoAttrContext();
35+
return instance;
36+
}
37+
38+
public void subscribeAttributeEvent(String deviceName, String attributeName, String baseName, TangoAttr_PV pv) throws DevFailed {
39+
String name = deviceName +"/" + attributeName;
40+
AttributeProxy attributeProxy;
41+
if (attributeProxys.get(baseName) == null) {
42+
attributeProxy = new AttributeProxy(name);
43+
subscribeAttributeEvent(baseName, attributeProxy, pv);
44+
attributeProxys.put(baseName,attributeProxy);
45+
}else {
46+
//nothing to do
47+
}
48+
49+
}
50+
51+
private void subscribeAttributeEvent(String baseName, AttributeProxy attributeProxy, TangoAttr_PV pv) throws DevFailed {
52+
AttributeInfoEx attribute_info_ex;
53+
try {
54+
attribute_info_ex = attributeProxy.get_info_ex();
55+
} catch (DevFailed e) {
56+
throw new RuntimeException(e);
57+
}
58+
59+
//obtain the type of attribute's value.
60+
AttributeTangoType type = AttributeTangoType.getTypeFromTango(attribute_info_ex.data_type);
61+
62+
types.putIfAbsent(baseName, type);
63+
64+
65+
//obtain the tango event type.
66+
EventType eventType = EventType.CHANGE_EVENT;
67+
EventProperties tangoObj = attribute_info_ex.events.getTangoObj();
68+
if (tangoObj.ch_event.abs_change.equals("Not specified") && tangoObj.ch_event.rel_change.equals("Not specified"))
69+
eventType = EventType.PERIODIC_EVENT;
70+
71+
//subscribe the tango event.
72+
int event_id;
73+
try {
74+
event_id = attributeProxy.subscribe_event(eventType.getValue(), pv.new TangoCallBack(type), new String[]{});
75+
} catch (DevFailed e) {
76+
throw new RuntimeException(e);
77+
}
78+
events.put(baseName, event_id);
79+
80+
}
81+
82+
public void unSubscribeAttributeEvent(String baseName) throws Exception {
83+
if (!attributeProxys.containsKey(baseName)){
84+
PV.logger.log(Level.WARNING, "Could not unsubscribe Tango attribute \"" + baseName
85+
+ "\" due to no Attribute Proxy.");
86+
throw new Exception("Tango attribute unsubscribe failed: no Attribute proxy connection.");
87+
}
88+
89+
AttributeProxy attributeProxy = attributeProxys.get(baseName);
90+
Integer event_id = events.get(baseName);
91+
if (event_id == null){
92+
PV.logger.log(Level.WARNING, "Could not unsubscribe Tango attribute \"" + baseName
93+
+ "\" due to no internal record of attribute");
94+
throw new Exception("Tango attribute unsubscribe failed: no attribute record.");
95+
}
96+
attributeProxy.getDeviceProxy().unsubscribe_event(event_id);
97+
attributeProxys.remove(baseName);
98+
events.remove(baseName);
99+
types.remove(baseName);
100+
}
101+
102+
103+
public void writeAttribute(String baseName, String attributeName, Object new_value) throws Exception {
104+
AttributeProxy attributeProxy = attributeProxys.get(baseName);
105+
AttributeTangoType type = types.get(baseName);
106+
if (type == null){
107+
PV.logger.log(Level.WARNING, "Could not find type of attribute :" + baseName);
108+
throw new Exception("Tango attribute write failed: attribute type not found.");
109+
}
110+
VType vType;
111+
String value;
112+
switch (type){
113+
case DEVBOOLEAN:
114+
vType = TangoTypeUtil.convert(new_value, VBoolean.class);
115+
value = TangoTypeUtil.ToString(vType);
116+
attributeProxy.write(new DeviceAttribute(attributeName, Boolean.parseBoolean(value)));
117+
break;
118+
case DEVLONG64:
119+
case DEVULONG64:
120+
vType = TangoTypeUtil.convert(new_value, VLong.class);
121+
value = TangoTypeUtil.ToString(vType);
122+
attributeProxy.write(new DeviceAttribute(attributeName, Long.parseLong(value)));
123+
break;
124+
case DEVSHORT:
125+
case DEVUSHORT:
126+
vType = TangoTypeUtil.convert(new_value, VShort.class);
127+
value = TangoTypeUtil.ToString(vType);
128+
attributeProxy.write(new DeviceAttribute(attributeName, Short.parseShort(value)));
129+
break;
130+
case DEVLONG:
131+
case DEVULONG:
132+
vType = TangoTypeUtil.convert(new_value, VInt.class);
133+
value = TangoTypeUtil.ToString(vType);
134+
attributeProxy.write(new DeviceAttribute(attributeName, Integer.parseInt(value)));
135+
break;
136+
case DEVFLOAT:
137+
vType = TangoTypeUtil.convert(new_value, VFloat.class);
138+
value = TangoTypeUtil.ToString(vType);
139+
attributeProxy.write(new DeviceAttribute(attributeName, Float.parseFloat(value)));
140+
break;
141+
case DEVDOUBLE:
142+
vType = TangoTypeUtil.convert(new_value, VDouble.class);
143+
value = TangoTypeUtil.ToString(vType);
144+
attributeProxy.write(new DeviceAttribute(attributeName, Double.parseDouble(value)));
145+
break;
146+
case DEVSTRING:
147+
vType = TangoTypeUtil.convert(new_value, VString.class);
148+
value = TangoTypeUtil.ToString(vType);
149+
attributeProxy.write(new DeviceAttribute(attributeName, value));
150+
break;
151+
case DEVUCHAR:
152+
vType = TangoTypeUtil.convert(new_value, VByte.class);
153+
value = TangoTypeUtil.ToString(vType);
154+
attributeProxy.write(new DeviceAttribute(attributeName, Byte.parseByte(value)));
155+
break;
156+
default:
157+
throw new IllegalArgumentException("Value " + new_value + " cannot be converted.");
158+
}
159+
160+
}
161+
162+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package org.phoebus.pv.tga;
2+
3+
import fr.esrf.Tango.DevFailed;
4+
import fr.esrf.TangoApi.CallBack;
5+
import fr.esrf.TangoApi.DeviceAttribute;
6+
import fr.esrf.TangoApi.events.EventData;
7+
import org.epics.vtype.*;
8+
import org.phoebus.pv.PV;
9+
import org.tango.attribute.AttributeTangoType;
10+
11+
import java.time.Instant;
12+
import java.util.logging.Level;
13+
14+
public class TangoAttr_PV extends PV {
15+
16+
private final String baseName;
17+
private String device;
18+
private String attribute;
19+
20+
21+
public TangoAttr_PV(String name, String baseName) throws Exception {
22+
super(name);
23+
this.baseName = baseName;
24+
parseRawName(baseName);
25+
TangoAttrContext.getInstance().subscribeAttributeEvent(device, attribute, baseName,this);
26+
}
27+
28+
29+
private void parseRawName(final String name) throws Exception {
30+
int pos = name.lastIndexOf('/');
31+
if (pos <= 0)
32+
throw new Exception("Invalid input:" + name);
33+
//Locate device name
34+
device = name.substring(0,pos);
35+
//Locate tango attribute
36+
attribute = name.substring(pos+1);
37+
}
38+
39+
40+
@Override
41+
protected void close()
42+
{
43+
try
44+
{
45+
TangoAttrContext.getInstance().unSubscribeAttributeEvent(baseName);
46+
}
47+
catch (Exception ex)
48+
{
49+
logger.log(Level.WARNING, "Failed to unsubscribe Tango Attribute from base name " + baseName);
50+
ex.printStackTrace();
51+
}
52+
}
53+
54+
@Override
55+
public void write(final Object new_value) throws Exception{
56+
if (new_value == null)
57+
throw new Exception(getName() + " got null");
58+
TangoAttrContext.getInstance().writeAttribute(baseName, attribute, new_value);
59+
}
60+
61+
62+
class TangoCallBack extends CallBack {
63+
private final AttributeTangoType type;
64+
65+
public TangoCallBack(AttributeTangoType type) {
66+
this.type = type;
67+
}
68+
69+
@Override
70+
public void push_event(EventData evt) {
71+
try {
72+
VType value;
73+
DeviceAttribute attr_value = evt.attr_value;
74+
Time time = Time.of(Instant.ofEpochMilli(attr_value.getTime()));
75+
switch (type){
76+
case DEVBOOLEAN:
77+
value = VBoolean.of(attr_value.extractBoolean(), Alarm.none(), time);
78+
notifyListenersOfValue(value);
79+
break;
80+
case DEVLONG64:
81+
value = VLong.of(attr_value.extractLong64(), Alarm.none(), time, Display.none());
82+
notifyListenersOfValue(value);
83+
break;
84+
case DEVULONG64:
85+
value = VLong.of(attr_value.extractULong64(), Alarm.none(), time, Display.none());
86+
notifyListenersOfValue(value);
87+
break;
88+
case DEVSHORT:
89+
value = VShort.of(attr_value.extractShort(), Alarm.none(), time, Display.none());
90+
notifyListenersOfValue(value);
91+
break;
92+
case DEVUSHORT:
93+
value = VInt.of(attr_value.extractUShort(),Alarm.none(), time, Display.none());
94+
notifyListenersOfValue(value);
95+
break;
96+
case DEVLONG:
97+
value = VInt.of(attr_value.extractLong(), Alarm.none(), time, Display.none());
98+
notifyListenersOfValue(value);
99+
break;
100+
case DEVULONG:
101+
value = VLong.of(attr_value.extractULong(), Alarm.none(), time, Display.none());
102+
notifyListenersOfValue(value);
103+
break;
104+
case DEVFLOAT:
105+
value = VFloat.of(attr_value.extractFloat(), Alarm.none(), time, Display.none());
106+
notifyListenersOfValue(value);
107+
break;
108+
case DEVDOUBLE:
109+
value = VDouble.of(attr_value.extractDouble(), Alarm.none(), time, Display.none());
110+
notifyListenersOfValue(value);
111+
break;
112+
case DEVSTRING:
113+
value = VString.of(attr_value.extractString(), Alarm.none(), time);
114+
notifyListenersOfValue(value);
115+
break;
116+
case DEVUCHAR:
117+
value = VShort.of(attr_value.extractUChar(), Alarm.none(), time,Display.none());
118+
notifyListenersOfValue(value);
119+
break;
120+
default:
121+
throw new IllegalArgumentException("Value " + evt.attr_value + " cannot be converted.");
122+
}
123+
}catch (DevFailed e){
124+
throw new RuntimeException(e);
125+
}
126+
}
127+
}
128+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.phoebus.pv.tga;
2+
3+
import org.phoebus.pv.PV;
4+
import org.phoebus.pv.PVFactory;
5+
6+
7+
public class TangoAttr_PVFactory implements PVFactory {
8+
9+
/** PV type implemented by this factory */
10+
final public static String TYPE = "tga";
11+
12+
@Override
13+
public String getType() {
14+
return TYPE;
15+
}
16+
17+
@Override
18+
public PV createPV(String name, String base_name) throws Exception {
19+
return new TangoAttr_PV(name, base_name);
20+
}
21+
22+
23+
}

0 commit comments

Comments
 (0)