package javaforce.controls; /** Monitors a PLC Tag. * * Auto-reconnects when disconnects. * * @author pquiring */ import java.util.*; import javaforce.*; public class Tag { /** Host (usually IP Address) */ public String host; /** Type of host (S7, AB, MB, NI) */ public Controller.types type; /** Tag name. */ public String tag; /** Size of tag. */ public Controller.sizes size; /** Color of tag (for reporting) */ public int color; /** int min/max values (for reporting) */ public int min, max; /** float min/max values (for reporting) */ public float fmin, fmax; /** Speed to poll data (delay = ms delay between polls) (min = 25ms) */ public int delay; /** Get user data. */ public Object getData(String key) { return user.get(key); } /** Set user data. */ public void setData(String key, Object value) { user.put(key, value); } /** Set host,type,tag,size,delay(ms). */ public void setTag(String host, Controller.types type, String tag, Controller.sizes size, int delay) { this.host = host; this.type = type; this.tag = tag; this.size = size; this.delay = delay; } private Controller c; private Timer timer; private Reader reader; private TagListener listener; private HashMap<String, Object> user = new HashMap<String, Object>(); private Tag parent; private int childIdx; private Object lock = new Object(); private ArrayList<Tag> children = new ArrayList<Tag>(); private byte[][] childData; private ArrayList<Tag> queue = new ArrayList<Tag>(); private boolean multiRead = true; /** Returns true if data type is float32 or float64 */ public boolean isFloat() { return size == Controller.sizes.float32 || size == Controller.sizes.float64; } /** Returns true is controller is Big Endian byte order. */ public boolean isBE() { switch (type) { case S7: return true; case AB: return false; case MB: return true; case NI: return true; default: return true; } } /** Returns true is controller is Little Endian byte order. */ public boolean isLE() { return !isBE(); } /** Enables reading multiple tags in one request (currently only S7 supported) */ public void setMultiRead(boolean state) { if (type != Controller.types.S7) return; if (true) return; //TODO!!! multiRead = state; } /** Adds a child tag and returns index. */ public int addChild(Tag child) { children.add(child); return children.size() - 1; } private void queue(Tag tag) { synchronized(queue) { queue.add(tag); } } /** Returns # of bytes tag uses. */ public int getSize() { switch (size) { case bit: return 1; case int8: return 1; case int16: return 2; case int32: return 4; case float32: return 4; case float64: return 8; } return 0; } public String getURL() { switch (type) { case S7: return "S7:" + host; case AB: return "AB:" + host; case MB: return "MB:" + host; case NI: return "NI:" + host; } return null; } public Controller getController() { if (parent != null) { return parent.c; } else { return c; } } public void setListener(TagListener listener) { this.listener = listener; } public String toString() { if (type == Controller.types.NI) { return host; } return tag; } public String getmin() { if (isFloat()) { return Float.toString(fmin); } else { return Integer.toString(min); } } public String getmax() { if (isFloat()) { return Float.toString(fmax); } else { return Integer.toString(max); } } private boolean startTimer() { if (parent == null) { childData = null; c = new Controller(); } else { c = null; } timer = new Timer(); reader = new Reader(); reader.tag = this; if (delay < 25) delay = 25; timer.scheduleAtFixedRate(reader, delay, delay); return true; } /** Start reading tag at interval (delay). */ public boolean start() { parent = null; return startTimer(); } /** Start reading tag at interval (delay) using another Tags connection. */ public boolean start(Tag parent) { this.parent = parent; if (parent != null) { if (parent.type != type) return false; childIdx = parent.addChild(this); } return startTimer(); } /** Stop monitoring tag value. */ public void stop() { if (timer != null) { timer.cancel(); timer = null; } if (reader != null) { reader = null; } disconnect(); } public boolean connect() { if (parent != null) return false; //wait for parent to connect if (c.connect(getURL())) return true; return false; } public void disconnect() { if (parent != null) return; if (c != null) { c.disconnect(); c = null; } } private String value = "0"; /** Returns current value (only valid if start() has been called). */ public String getValue() { return value; } /** Returns current value as int (only valid if start() has been called). */ public int intValue() { return Integer.valueOf(value); } /** Returns current value as float (only valid if start() has been called). */ public float floatValue() { return Float.valueOf(value); } /** Returns current value as double (float64) (only valid if start() has been called). */ public double doubleValue() { return Double.valueOf(value); } /** Reads value directly. */ public byte[] read() { if (parent != null) { if (parent.c == null) return null; if (multiRead) { return parent.read(childIdx); } else { //queue read with parent to prevent some threads from starving synchronized(lock) { parent.queue(this); try {lock.wait();} catch (Exception e) {} return parent.c.read(tag); } } } else { if (multiRead && type == Controller.types.S7 && children.size() > 0) { int cnt = children.size(); String tags[] = new String[cnt+1]; tags[cnt] = tag; for(int a=0;a<cnt;a++) { tags[a] = children.get(a).tag; } childData = c.read(tags); if (childData == null) return null; return childData[cnt]; } else { //allow queued children to proceed synchronized(queue) { while (queue.size() > 0) { Tag child = queue.remove(0); synchronized(child.lock) { child.lock.notify(); } } } return c.read(tag); } } } /** Writes data to tag. */ public void write(byte data[]) { if (parent != null) { if (parent.c == null) return; parent.c.write(tag, data); } else { c.write(tag, data); } } private byte[] read(int idx) { if (childData == null || idx >= childData.length) return null; return childData[idx]; } private static class Reader extends TimerTask { public Tag tag; public byte data[]; public void run() { try { String lastValue = tag.value; if (tag.parent == null) { if (!tag.c.isConnected()) { if (!tag.connect()) { return; } } } data = tag.read(); if (data == null) { System.out.println("Error:" + System.currentTimeMillis() + ":data==null:host=" + tag.host + ":tag=" + tag.tag); return; } if (tag.isBE()) { switch (tag.size) { case bit: tag.value = data[0] == 0 ? "0" : "1"; break; case int8: tag.value = Byte.toString(data[0]); break; case int16: tag.value = Short.toString((short)BE.getuint16(data, 0)); break; case int32: tag.value = Integer.toString(BE.getuint32(data, 0)); break; case float32: tag.value = Float.toString(Float.intBitsToFloat(BE.getuint32(data, 0))); break; case float64: tag.value = Double.toString(Double.longBitsToDouble(BE.getuint64(data, 0))); break; } } else { switch (tag.size) { case bit: tag.value = data[0] == 0 ? "0" : "1"; break; case int8: tag.value = Byte.toString(data[0]); break; case int16: tag.value = Short.toString((short)LE.getuint16(data, 0)); break; case int32: tag.value = Integer.toString(LE.getuint32(data, 0)); break; case float32: tag.value = Float.toString(Float.intBitsToFloat(LE.getuint32(data, 0))); break; case float64: tag.value = Double.toString(Double.longBitsToDouble(LE.getuint64(data, 0))); break; } } if (tag.listener == null) return; if (lastValue == null || !tag.value.equals(lastValue)) { tag.listener.tagChanged(tag); } } catch (Exception e) { JFLog.log(e); } } } }