BasicProcessImage.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * ============================================================================
  3. * GNU General Public License
  4. * ============================================================================
  5. *
  6. * Copyright (C) 2006-2011 Serotonin Software Technologies Inc. http://serotoninsoftware.com
  7. * @author Matthew Lohbihler
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. package com.serotonin.cdc.modbus4j;
  22. import java.util.ArrayList;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.Map;
  27. import com.serotonin.cdc.modbus4j.base.ModbusUtils;
  28. import com.serotonin.cdc.modbus4j.base.RangeAndOffset;
  29. import com.serotonin.cdc.modbus4j.code.RegisterRange;
  30. import com.serotonin.cdc.modbus4j.exception.IllegalDataAddressException;
  31. import com.serotonin.cdc.modbus4j.exception.ModbusIdException;
  32. import com.serotonin.cdc.modbus4j.locator.BaseLocator;
  33. import com.serotonin.cdc.modbus4j.locator.NumericLocator;
  34. import com.serotonin.cdc.modbus4j.locator.StringLocator;
  35. public class BasicProcessImage implements ProcessImage {
  36. private final int slaveId;
  37. private boolean allowInvalidAddress = false;
  38. private short invalidAddressValue = 0;
  39. private final Map coils = new HashMap();
  40. private final Map inputs = new HashMap();
  41. private final Map holdingRegisters = new HashMap();
  42. private final Map inputRegisters = new HashMap();
  43. private final List writeListeners = new ArrayList();
  44. private byte exceptionStatus;
  45. public BasicProcessImage(int slaveId) {
  46. ModbusUtils.validateSlaveId(slaveId, false);
  47. this.slaveId = slaveId;
  48. }
  49. //Override
  50. public int getSlaveId() {
  51. return slaveId;
  52. }
  53. public synchronized void addListener(ProcessImageListener l) {
  54. writeListeners.add(l);
  55. }
  56. public synchronized void removeListener(ProcessImageListener l) {
  57. writeListeners.remove(l);
  58. }
  59. public boolean isAllowInvalidAddress() {
  60. return allowInvalidAddress;
  61. }
  62. public void setAllowInvalidAddress(boolean allowInvalidAddress) {
  63. this.allowInvalidAddress = allowInvalidAddress;
  64. }
  65. public short getInvalidAddressValue() {
  66. return invalidAddressValue;
  67. }
  68. public void setInvalidAddressValue(short invalidAddressValue) {
  69. this.invalidAddressValue = invalidAddressValue;
  70. }
  71. //
  72. // /
  73. // / Additional convenience methods.
  74. // /
  75. //
  76. public void setExceptionStatus(byte exceptionStatus) {
  77. this.exceptionStatus = exceptionStatus;
  78. }
  79. //
  80. // Binaries
  81. public void setBinary(int registerId, boolean value) {
  82. RangeAndOffset rao = new RangeAndOffset(registerId);
  83. setBinary(rao.getRange(), rao.getOffset(), value);
  84. }
  85. public void setBinary(int range, int offset, boolean value) {
  86. if (range == RegisterRange.COIL_STATUS)
  87. setCoil(offset, value);
  88. else if (range == RegisterRange.INPUT_STATUS)
  89. setInput(offset, value);
  90. else
  91. throw new ModbusIdException("Invalid range to set binary: " + range);
  92. }
  93. //
  94. // Numerics
  95. public synchronized void setNumeric(int registerId, int dataType, Number value) {
  96. RangeAndOffset rao = new RangeAndOffset(registerId);
  97. setNumeric(rao.getRange(), rao.getOffset(), dataType, value);
  98. }
  99. public synchronized void setNumeric(int range, int offset, int dataType, Number value) {
  100. short[] registers = new NumericLocator(slaveId, range, offset, dataType).valueToShorts(value);
  101. // Write the value.
  102. if (range == RegisterRange.HOLDING_REGISTER)
  103. setHoldingRegister(offset, registers);
  104. else if (range == RegisterRange.INPUT_REGISTER)
  105. setInputRegister(offset, registers);
  106. else
  107. throw new ModbusIdException("Invalid range to set register: " + range);
  108. }
  109. public synchronized void setString(int range, int offset, int dataType, int registerCount, String s) {
  110. short[] registers = new StringLocator(slaveId, range, offset, dataType, registerCount).valueToShorts(s);
  111. // Write the value.
  112. if (range == RegisterRange.HOLDING_REGISTER)
  113. setHoldingRegister(offset, registers);
  114. else if (range == RegisterRange.INPUT_REGISTER)
  115. setInputRegister(offset, registers);
  116. else
  117. throw new ModbusIdException("Invalid range to set register: " + range);
  118. }
  119. public synchronized void setHoldingRegister(int offset, short[] registers) {
  120. validateOffset(offset);
  121. for (int i = 0; i < registers.length; i++)
  122. setHoldingRegister(offset + i, registers[i]);
  123. }
  124. public synchronized void setInputRegister(int offset, short[] registers) {
  125. validateOffset(offset);
  126. for (int i = 0; i < registers.length; i++)
  127. setInputRegister(offset + i, registers[i]);
  128. }
  129. //
  130. // Bits
  131. public synchronized void setBit(int range, int offset, int bit, boolean value) {
  132. if (range == RegisterRange.HOLDING_REGISTER)
  133. setHoldingRegisterBit(offset, bit, value);
  134. else if (range == RegisterRange.INPUT_REGISTER)
  135. setInputRegisterBit(offset, bit, value);
  136. else
  137. throw new ModbusIdException("Invalid range to set register: " + range);
  138. }
  139. public synchronized void setHoldingRegisterBit(int offset, int bit, boolean value) {
  140. validateBit(bit);
  141. short s;
  142. try {
  143. s = getHoldingRegister(offset);
  144. }
  145. catch (IllegalDataAddressException e) {
  146. s = 0;
  147. }
  148. setHoldingRegister(offset, setBit(s, bit, value));
  149. }
  150. public synchronized void setInputRegisterBit(int offset, int bit, boolean value) {
  151. validateBit(bit);
  152. short s;
  153. try {
  154. s = getInputRegister(offset);
  155. }
  156. catch (IllegalDataAddressException e) {
  157. s = 0;
  158. }
  159. setInputRegister(offset, setBit(s, bit, value));
  160. }
  161. public boolean getBit(int range, int offset, int bit) throws IllegalDataAddressException {
  162. if (range == RegisterRange.HOLDING_REGISTER)
  163. return getHoldingRegisterBit(offset, bit);
  164. if (range == RegisterRange.INPUT_REGISTER)
  165. return getInputRegisterBit(offset, bit);
  166. throw new ModbusIdException("Invalid range to get register: " + range);
  167. }
  168. public boolean getHoldingRegisterBit(int offset, int bit) throws IllegalDataAddressException {
  169. validateBit(bit);
  170. return getBit(getHoldingRegister(offset), bit);
  171. }
  172. public boolean getInputRegisterBit(int offset, int bit) throws IllegalDataAddressException {
  173. validateBit(bit);
  174. return getBit(getInputRegister(offset), bit);
  175. }
  176. public Number getNumeric(int range, int offset, int dataType) throws IllegalDataAddressException {
  177. return (Number) getRegister(new NumericLocator(slaveId, range, offset, dataType));
  178. }
  179. public String getString(int range, int offset, int dataType, int registerCount) throws IllegalDataAddressException {
  180. return (String) getRegister(new StringLocator(slaveId, range, offset, dataType, registerCount));
  181. }
  182. /***
  183. * public String getString(int range, int offset, int dataType, int registerCount, Charset charset) throws
  184. * IllegalDataAddressException { return (String)getRegister(new StringLocator(slaveId, range, offset, dataType,
  185. * registerCount, charset)); }
  186. ***/
  187. public synchronized Object getRegister(BaseLocator locator) throws IllegalDataAddressException {
  188. int words = locator.getRegisterCount();
  189. byte[] b = new byte[locator.getRegisterCount() * 2];
  190. for (int i = 0; i < words; i++) {
  191. short s;
  192. if (locator.getRange() == RegisterRange.INPUT_REGISTER)
  193. s = getInputRegister(locator.getOffset() + i);
  194. else if (locator.getRange() == RegisterRange.HOLDING_REGISTER)
  195. s = getHoldingRegister(locator.getOffset() + i);
  196. else if (allowInvalidAddress)
  197. s = invalidAddressValue;
  198. else
  199. throw new IllegalDataAddressException();
  200. b[i * 2] = ModbusUtils.toByte(s, true);
  201. b[i * 2 + 1] = ModbusUtils.toByte(s, false);
  202. }
  203. return locator.bytesToValueRealOffset(b, 0);
  204. }
  205. //
  206. //
  207. // ProcessImage interface
  208. //
  209. //
  210. // Coils
  211. public synchronized boolean getCoil(int offset) throws IllegalDataAddressException {
  212. return getBoolean(offset, coils);
  213. }
  214. public synchronized void setCoil(int offset, boolean value) {
  215. validateOffset(offset);
  216. coils.put(new Integer(offset), new Boolean(value));
  217. }
  218. public synchronized void writeCoil(int offset, boolean value) throws IllegalDataAddressException {
  219. boolean old = getBoolean(offset, coils);
  220. setCoil(offset, value);
  221. for (Iterator iter = writeListeners.iterator(); iter.hasNext();) {
  222. ProcessImageListener l = (ProcessImageListener) iter.next();
  223. l.coilWrite(offset, old, value);
  224. }
  225. }
  226. //
  227. // Inputs
  228. public synchronized boolean getInput(int offset) throws IllegalDataAddressException {
  229. return getBoolean(offset, inputs);
  230. }
  231. public synchronized void setInput(int offset, boolean value) {
  232. validateOffset(offset);
  233. inputs.put(new Integer(offset), new Boolean(value));
  234. }
  235. //
  236. // Holding registers
  237. public synchronized short getHoldingRegister(int offset) throws IllegalDataAddressException {
  238. return getShort(offset, holdingRegisters);
  239. }
  240. public synchronized void setHoldingRegister(int offset, short value) {
  241. validateOffset(offset);
  242. holdingRegisters.put(new Integer(offset), new Short(value));
  243. }
  244. public synchronized void writeHoldingRegister(int offset, short value) throws IllegalDataAddressException {
  245. short old = getShort(offset, holdingRegisters);
  246. setHoldingRegister(offset, value);
  247. for (Iterator iter = writeListeners.iterator(); iter.hasNext();) {
  248. ProcessImageListener l = (ProcessImageListener) iter.next();
  249. l.holdingRegisterWrite(offset, old, value);
  250. }
  251. }
  252. //
  253. // Input registers
  254. public synchronized short getInputRegister(int offset) throws IllegalDataAddressException {
  255. return getShort(offset, inputRegisters);
  256. }
  257. public synchronized void setInputRegister(int offset, short value) {
  258. validateOffset(offset);
  259. inputRegisters.put(new Integer(offset), new Short(value));
  260. }
  261. //
  262. // Exception status
  263. public byte getExceptionStatus() {
  264. return exceptionStatus;
  265. }
  266. //
  267. // Report slave id
  268. public byte[] getReportSlaveIdData() {
  269. return new byte[0];
  270. }
  271. //
  272. //
  273. // Private
  274. //
  275. private short getShort(int offset, Map map) throws IllegalDataAddressException {
  276. Short value = (Short) map.get(new Integer(offset));
  277. if (value == null) {
  278. if (allowInvalidAddress)
  279. return invalidAddressValue;
  280. throw new IllegalDataAddressException();
  281. }
  282. return value.shortValue();
  283. }
  284. private boolean getBoolean(int offset, Map map) throws IllegalDataAddressException {
  285. Boolean value = (Boolean) map.get(new Integer(offset));
  286. if (value == null) {
  287. if (allowInvalidAddress)
  288. return false;
  289. throw new IllegalDataAddressException();
  290. }
  291. return value.booleanValue();
  292. }
  293. private void validateOffset(int offset) {
  294. if (offset < 0 || offset > 65535)
  295. throw new ModbusIdException("Invalid offset: " + offset);
  296. }
  297. private void validateBit(int bit) {
  298. if (bit < 0 || bit > 15)
  299. throw new ModbusIdException("Invalid bit: " + bit);
  300. }
  301. private short setBit(short s, int bit, boolean value) {
  302. return (short) (s | ((value ? 1 : 0) << bit));
  303. }
  304. private boolean getBit(short s, int bit) {
  305. return ((s >> bit) & 0x1) == 1;
  306. }
  307. }