├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
└── org.eclipse.jdt.core.prefs
├── LICENSE
├── README.md
├── build
└── omron-fins.jar
├── example
└── com
│ └── delmesoft
│ └── cp1l
│ └── fins
│ └── Cp1lFinsExample.java
└── src
└── com
└── delmesoft
└── cp1l
└── fins
├── CIO.java
├── ControllerData.java
├── Cp1lFins.java
├── Cp1lFinsImpl.java
├── Hashcode.java
└── InputListener.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | omron-fins
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.release=disabled
12 | org.eclipse.jdt.core.compiler.source=1.8
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Sergio Soriano
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # omron-fins
2 | Java implementation of Omron FINS communication protocol
3 |
4 | # Documentation
5 |
6 | ## `public interface Cp1lFins`
7 |
8 | ## `void connect() throws Exception`
9 |
10 | Establishes connection with the device
11 |
12 | * **Exceptions:** `Exception` — Error opening connection
13 |
14 | ## `boolean isConnected()`
15 |
16 | Connection status
17 |
18 | * **Returns:** true connected
19 |
20 | ## `void disconnect()`
21 |
22 | Close current connection
23 |
24 | ## `ControllerData getControllerData() throws Exception`
25 |
26 | Controller information data
27 |
28 | * **Returns:** ControllerData
29 | * **Exceptions:** `Exception` — Error getting ControllerData
30 |
31 | ## `boolean read(int position, int bit) throws Exception`
32 |
33 | Read the state of the indicated bit
34 |
35 | * **Parameters:**
36 | * `position` — Memory position
37 | * `bit` — Bit position
38 | * **Returns:** bit status
39 | * **Exceptions:** `Exception` — Error reading
40 |
41 | ## `boolean read(CIO cio) throws Exception`
42 |
43 | Read the state of the indicated bit
44 |
45 | * **Parameters:** `cio` — Channel Input Output
46 | * **Returns:** bit status
47 | * **Exceptions:** `Exception` — Error reading
48 |
49 | ## `void write(int position, int bit, boolean value) throws Exception`
50 |
51 | Write the status of the indicated input
52 |
53 | * **Parameters:**
54 | * `position` — Memory position
55 | * `bit` — Bit position
56 | * `value` — New state
57 | * **Exceptions:** `Exception` — Error writing
58 |
59 | ## `void write(CIO cio, boolean value) throws Exception`
60 |
61 | Write the status of the indicated input
62 |
63 | * **Parameters:**
64 | * `cio` — Channel Input Output
65 | * `value` — new state
66 | * **Exceptions:** `Exception` — Exception Error writing
67 |
68 | ## `void setInputListener(InputListener listener)`
69 |
70 | * **Parameters:** `listener` — Interface to notify subscription events
71 |
72 | ## `InputListener getInputListener()`
73 |
74 | * **Returns:** Interface where subscription events are notified
75 |
76 | ## `void subscribe(CIO cio)`
77 |
78 | Adds a subscription to the state change events of the indicated bit
79 |
80 | * **Parameters:** `cio` — Channel Input Output
81 |
82 | ## `boolean unsuscribe(CIO cio)`
83 |
84 | Remove bit subscription
85 |
86 | * **Parameters:** `cio` — Channel Input Output
87 | * **Returns:** true if there was a subscription
88 |
89 | ## `void subscribe(int position, int bit)`
90 |
91 | Adds a subscription to the state change events of the indicated bit
92 |
93 | * **Parameters:**
94 | * `position` — Memory position
95 | * `bit` — Bit position
96 |
97 | ## `void unsuscribe(int position, int bit)`
98 |
99 | Remove bit subscription
100 |
101 | * **Parameters:**
102 | * `position` — Memory position
103 | * `bit` — Bit position
104 |
105 | ## `void unsuscribeAll()`
106 |
107 | Remove all bit subscriptions
108 |
109 | 2021 Sergio Soriano - www.sergiosoriano.com
110 |
--------------------------------------------------------------------------------
/build/omron-fins.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiss/omron-fins/1b5ae2017ac0d455803d8a1a44f93f6d64e454fa/build/omron-fins.jar
--------------------------------------------------------------------------------
/example/com/delmesoft/cp1l/fins/Cp1lFinsExample.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | public class Cp1lFinsExample {
4 |
5 | public static void main(String[] args) throws Exception {
6 |
7 | String host = "169.254.0.10";
8 | int port = 9600;
9 |
10 | Cp1lFins cp1lFins = new Cp1lFinsImpl(host, port);
11 | cp1lFins.setInputListener(new InputListener() {
12 | @Override
13 | public void onEvent(int ch, int pin, boolean state) {
14 | System.out.printf("onEvent: ch=%d, pin=%d, state=%b\n", ch, pin, state);
15 | }
16 |
17 | @Override
18 | public void onError(Exception e) {
19 | e.printStackTrace();
20 | }
21 | });
22 |
23 | // Subscribe to input
24 | cp1lFins.subscribe(new CIO(0, 0)); // channel 0 pin 0 (0.00)
25 | cp1lFins.subscribe(new CIO(1, 0)); // channel 1 pin 0 (1.00)
26 |
27 | cp1lFins.subscribe(new CIO(102, 02)); // Custom
28 |
29 | cp1lFins.connect();
30 | System.out.printf("Connected (host: '%s', port: %d)\n", host, port);
31 | System.out.println(cp1lFins.getControllerData());
32 |
33 | // Write Output
34 | cp1lFins.write(101, 00, true); // channel 100 pin 0
35 |
36 | boolean state = false;
37 | for(int i = 0; i < 10; ++i) { // blink
38 | Thread.sleep(500);
39 | state = !state;
40 | cp1lFins.write(100, 01, state); // channel 100 pin 1
41 | }
42 |
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/com/delmesoft/cp1l/fins/CIO.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | /**
4 | * 2021 Sergio S. - sergi.ss4@gmail.com
5 | * @author Sergio S.
6 | *
7 | */
8 | public class CIO { // Channel Input Output
9 |
10 | private int position;
11 | private int bit;
12 |
13 | transient boolean state;
14 |
15 | public CIO() {}
16 |
17 | public CIO(int position, int bit) {
18 | this.position = position;
19 | this.bit = bit;
20 | }
21 |
22 | public int getPosition() {
23 | return position;
24 | }
25 |
26 | public void setPosition(int position) {
27 | this.position = position;
28 | }
29 |
30 | public int getBit() {
31 | return bit;
32 | }
33 |
34 | public void setBit(int bit) {
35 | this.bit = bit;
36 | }
37 |
38 | @Override
39 | public int hashCode() {
40 | final int prime = 31;
41 | int result = 1;
42 | result = prime * result + bit;
43 | result = prime * result + position;
44 | return result;
45 | }
46 |
47 | @Override
48 | public boolean equals(Object obj) {
49 | if (this == obj)
50 | return true;
51 | if (obj == null)
52 | return false;
53 | if (getClass() != obj.getClass())
54 | return false;
55 | CIO other = (CIO) obj;
56 | if (bit != other.bit)
57 | return false;
58 | if (position != other.position)
59 | return false;
60 | return true;
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | StringBuilder builder = new StringBuilder();
66 | builder.append("CIO [position=");
67 | builder.append(position);
68 | builder.append(", bit=");
69 | builder.append(bit);
70 | builder.append("]");
71 | return builder.toString();
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/com/delmesoft/cp1l/fins/ControllerData.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | /**
4 | * 2021 Sergio S. - sergi.ss4@gmail.com
5 | * @author Sergio
6 | *
7 | */
8 | public class ControllerData {
9 |
10 | private String model;
11 |
12 | private String version;
13 |
14 | public ControllerData() {}
15 |
16 | public ControllerData(String model, String version) {
17 | this.model = model;
18 | this.version = version;
19 | }
20 |
21 | public String getModel() {
22 | return model;
23 | }
24 |
25 | public void setModel(String model) {
26 | this.model = model;
27 | }
28 |
29 | public String getVersion() {
30 | return version;
31 | }
32 |
33 | public void setVersion(String version) {
34 | this.version = version;
35 | }
36 |
37 | @Override
38 | public String toString() {
39 | StringBuilder builder = new StringBuilder();
40 | builder.append("ControllerData [model=");
41 | builder.append(model);
42 | builder.append(", version=");
43 | builder.append(version);
44 | builder.append("]");
45 | return builder.toString();
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/com/delmesoft/cp1l/fins/Cp1lFins.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | /**
4 | * 2021 Sergio S. - sergi.ss4@gmail.com
5 | * @author Sergio S.
6 | *
7 | */
8 | public interface Cp1lFins {
9 |
10 | /**
11 | * Establishes connection with the device
12 | * @throws Exception Error opening connection
13 | */
14 | void connect() throws Exception;
15 |
16 | /**
17 | * Connection status
18 | * @return true connected, false disconnected
19 | */
20 | boolean isConnected();
21 |
22 | /**
23 | * Close current connection
24 | */
25 | void disconnect();
26 |
27 | /**
28 | * Controller information data
29 | * @return ControllerData
30 | * @throws Exception Error getting ControllerData
31 | */
32 | ControllerData getControllerData() throws Exception;
33 |
34 | /**
35 | * Read the state of the indicated bit
36 | * @param position Memory position
37 | * @param bit Bit position
38 | * @return bit status
39 | * @throws Exception Error reading
40 | */
41 | boolean read(int position, int bit) throws Exception;
42 |
43 | /**
44 | * Read the state of the indicated bit
45 | * @param cio Channel Input Output
46 | * @return bit status
47 | * @throws Exception Error reading
48 | */
49 | boolean read(CIO cio) throws Exception;
50 |
51 | /**
52 | * Write the status of the indicated input
53 | * @param position Memory position
54 | * @param bit Bit position
55 | * @param value New state
56 | * @throws Exception Error writing
57 | */
58 | void write(int position, int bit, boolean value) throws Exception;
59 |
60 | /**
61 | * Write the status of the indicated input
62 | * @param cio Channel Input Output
63 | * @param value
64 | * @throws Exception Exception Error writing
65 | */
66 | void write(CIO cio, boolean value) throws Exception;
67 |
68 | /**
69 | *
70 | * @param listener
71 | */
72 | void setInputListener(InputListener listener);
73 |
74 | /**
75 | *
76 | * @return
77 | */
78 | InputListener getInputListener();
79 |
80 | /**
81 | * Adds a subscription to the state change events of the indicated bit
82 | * @param cio Channel Input Output
83 | */
84 | void subscribe(CIO cio);
85 |
86 | /**
87 | * Remove bit subscription
88 | * @param cio Channel Input Output
89 | * @return true if there was a subscription
90 | */
91 | boolean unsuscribe(CIO cio);
92 |
93 | /**
94 | * Adds a subscription to the state change events of the indicated bit
95 | * @param position
96 | * @param bit
97 | */
98 | void subscribe(int position, int bit);
99 |
100 | /**
101 | * Remove bit subscription
102 | * @param position
103 | * @param bit
104 | */
105 | void unsuscribe(int position, int bit);
106 |
107 | /**
108 | * Remove all bit subscriptions
109 | */
110 | void unsuscribeAll();
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/com/delmesoft/cp1l/fins/Cp1lFinsImpl.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | import java.io.EOFException;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 | import java.net.Socket;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * 2021 Sergio S. - sergi.ss4@gmail.com
13 | * @author Sergio S.
14 | *
15 | */
16 | public class Cp1lFinsImpl implements Cp1lFins {
17 |
18 | private static final int TIMEOUT = 10_000;
19 |
20 | public static final byte READ_ID = 2;
21 | public static final byte WRITE_ID = 4;
22 |
23 | private byte[] tmp = new byte[1024];
24 |
25 | private String host;
26 | private int port;
27 |
28 | private Socket socket;
29 | private InputStream is;
30 | private OutputStream os;
31 |
32 | private Thread thread;
33 | private InputListener listener;
34 |
35 | private Map cioMap = new HashMap<>();
36 |
37 | private long refreshTime;
38 |
39 | public Cp1lFinsImpl() {
40 | this("169.254.0.1");
41 | }
42 |
43 | public Cp1lFinsImpl(String host) {
44 | this(host, 9600);
45 | }
46 |
47 | public Cp1lFinsImpl(String host, int port) {
48 | this.host = host;
49 | this.port = port;
50 |
51 | this.refreshTime = 25; // Default time
52 | }
53 |
54 | @Override
55 | public synchronized void connect() throws Exception {
56 |
57 | if(!isConnected()) {
58 | socket = new Socket(host, port);
59 | socket.setSoTimeout(TIMEOUT);
60 | is = socket.getInputStream();
61 | os = socket.getOutputStream();
62 |
63 | byte[] message = {
64 | 0x46, 0x49, 0x4E, 0x53, // Header
65 | 0x00, 0x00, 0x00, 0x0C, // CMD length
66 | 0x00, 0x00, 0x00, 0x00, // Frame command
67 | 0x00, 0x00, 0x00, 0x00, // Error
68 | 0x00, 0x00, 0x00, 0x00
69 | };
70 |
71 | os.write(message, 0, 20);
72 | os.flush();
73 |
74 | int n = readResponse(tmp);
75 | if(n != 24 || tmp[23] != 1) { // check
76 | disconnect();
77 | throw new RuntimeException("connection message error");
78 | }
79 | thread = new Thread() {
80 | public void run() {
81 |
82 | try {
83 |
84 | boolean value;
85 | // Initial state
86 | synchronized (cioMap) {
87 | for (CIO cio : cioMap.values()) { // iterate
88 | value = read(cio.getPosition(), cio.getBit()); // read state
89 | cio.state = value; // update state
90 | }
91 | }
92 |
93 | while (!isInterrupted()) {
94 | Thread.sleep(refreshTime);
95 | // Update state
96 | synchronized (cioMap) {
97 | for (CIO cio : cioMap.values()) { // iterate
98 | value = read(cio.getPosition(), cio.getBit()); // read state
99 | if (value != cio.state) { // check
100 | cio.state = value; // update state
101 | if (listener != null) listener.onEvent(cio.getPosition(), cio.getBit(), value); // notify
102 | }
103 | }
104 | }
105 | }
106 |
107 | } catch (InterruptedException e) {
108 | // Ignore
109 | } catch (Exception e) {
110 | if(listener != null && isConnected()) {
111 | listener.onError(e);
112 | }
113 | } finally {
114 | disconnect();
115 | }
116 | }
117 | };
118 | thread.start();
119 | }
120 |
121 | }
122 |
123 | @Override
124 | public synchronized boolean isConnected() {
125 | return socket != null;
126 | }
127 |
128 | @Override
129 | public synchronized void disconnect() {
130 | if(isConnected()) {
131 | try {
132 | thread.interrupt();
133 | } catch (Exception e) {
134 | } finally {
135 | thread = null;
136 | }
137 | try {
138 | socket.close();
139 | } catch (Exception e) {
140 | } finally {
141 | socket = null;
142 | is = null;
143 | os = null;
144 | }
145 | }
146 | }
147 |
148 | @Override
149 | public synchronized ControllerData getControllerData() throws Exception {
150 |
151 | byte[] message = {
152 | 0x46, 0x49, 0x4E, 0x53, // Header
153 | 0x00, 0x00, 0x00, 0x15, // CMD length
154 | 0x00, 0x00, 0x00, 0x02, // Frame command
155 | 0x00, 0x00, 0x00, 0x00, // Error
156 | /* 0 */ (byte) 0x80, // ICF - Display frame information -
157 | /* 1 */ 0x00, // RSV - Reserved by system -
158 | /* 2 */ 0x02, // GCT - Permissible number of gateways -
159 | /* 3 */ 0x00, // DNA - Destination network address -
160 | /* 4 */ 0x01, // DA1 - Ethernet Unit FINS NODE NUMBER -
161 | /* 5 */ 0x00, // DA2 - Destination unit address -
162 | /* 6 */ 0x00, // SNA - Source network address -
163 | /* 7 */ 0x00, // SA1 - WS FINS NODE NUMBER -
164 | /* 8 */ 0x00, // SA2 - Source unit address -
165 | /* 9 */ READ_ID, // SID - Service ID -
166 | /* ------- FINS COMMAND -------------- */
167 | /* 10 */ 0x05, // MRC - Main Request code -
168 | /* 11 */ 0x01, // SRC - Sub-request code -
169 | /* ------- SEND DATA ----------------- */
170 | /* 12 */ 0x00, // VARIABLE TYPE: DM
171 | };
172 |
173 | os.write(message, 0, 29);
174 | os.flush();
175 |
176 | readResponse(tmp);
177 | if(tmp[25] != READ_ID || tmp[23] != 1) { // check
178 | throw new RuntimeException("read message error");
179 | }
180 |
181 | String model = new String(tmp, 30, 20).trim();
182 | String version = new String(tmp, 50, 10).trim();
183 | // String v2 = new String(tmp, 60, 10).trim();
184 | return new ControllerData(model, version);
185 | }
186 |
187 | @Override
188 | public boolean read(CIO cio) throws Exception {
189 | return read(cio.getPosition(), cio.getBit());
190 | }
191 |
192 | @Override
193 | public synchronized boolean read(int pos, int bit) throws Exception {
194 |
195 | byte[] message = {
196 | 0x46, 0x49, 0x4E, 0x53, // Header
197 | 0x00, 0x00, 0x00, 0x1A, // CMD length
198 | 0x00, 0x00, 0x00, 0x02, // Frame command
199 | 0x00, 0x00, 0x00, 0x00, // Error
200 | /* 0 */ (byte) 0x80, // ICF - Display frame information -
201 | /* 1 */ 0x00, // RSV - Reserved by system -
202 | /* 2 */ 0x02, // GCT - Permissible number of gateways -
203 | /* 3 */ 0x00, // DNA - Destination network address -
204 | /* 4 */ 0x01, // DA1 - Ethernet Unit FINS NODE NUMBER -
205 | /* 5 */ 0x00, // DA2 - Destination unit address -
206 | /* 6 */ 0x00, // SNA - Source network address -
207 | /* 7 */ 0x00, // SA1 - WS FINS NODE NUMBER -
208 | /* 8 */ 0x00, // SA2 - Source unit address -
209 | /* 9 */ READ_ID, // SID - Service ID -
210 | /* ------- FINS COMMAND -------------- */
211 | /* 10 */ 0x01, // MRC - Main Request code -
212 | /* 11 */ 0x01, // SRC - Sub-request code - (READ)
213 | /* ------- SEND DATA ----------------- */
214 | /* 12 */ 0x30, // VARIABLE TYPE: DM
215 | /* 13 */ 0x00,
216 | /* 14 */ (byte) pos, // READ START ADDRESS
217 | /* 15 */ (byte) bit,
218 | /* 16 */ 0x00, // WORDS READ
219 | /* 17 */ 0x01
220 | };
221 |
222 | os.write(message, 0, 34);
223 | os.flush();
224 |
225 | int n = readResponse(tmp);
226 | if(n != 31 || tmp[25] != READ_ID || tmp[23] != 1) { // check
227 | throw new RuntimeException("read message error");
228 | }
229 | return tmp[30] == 1;
230 | }
231 |
232 | @Override
233 | public void write(CIO cio, boolean value) throws Exception {
234 | write(cio.getPosition(), cio.getBit(), value);
235 | }
236 |
237 | @Override
238 | public synchronized void write(int pos, int bit, boolean value) throws Exception {
239 |
240 | byte[] message = {
241 | 0x46, 0x49, 0x4E, 0x53, // Header
242 | 0x00, 0x00, 0x00, 0x1B, // CMD length
243 | 0x00, 0x00, 0x00, 0x02, // Frame command
244 | 0x00, 0x00, 0x00, 0x00, // Error
245 | /* 0 */ (byte) 0x80, // ICF - Display frame information -
246 | /* 1 */ 0x00, // RSV - Reserved by system -
247 | /* 2 */ 0x02, // GCT - Permissible number of gateways -
248 | /* 3 */ 0x00, // DNA - Destination network address -
249 | /* 4 */ 0x01, // DA1 - Ethernet Unit FINS NODE NUMBER -
250 | /* 5 */ 0x00, // DA2 - Destination unit address -
251 | /* 6 */ 0x00, // SNA - Source network address -
252 | /* 7 */ 0x00, // SA1 - WS FINS NODE NUMBER -
253 | /* 8 */ 0x00, // SA2 - Source unit address -
254 | /* 9 */ WRITE_ID, // SID - Service ID -
255 | /* ------- FINS COMMAND -------------- */
256 | /* 10 */ 0x01, // MRC - Main Request code -
257 | /* 11 */ 0x02, // SRC - Sub-request code - (WRITE)
258 | /* ------- SEND DATA ----------------- */
259 | /* 12 */ 0x30, // VARIABLE TYPE: DM
260 | /* 13 */ 0x00,
261 | /* 14 */ (byte) pos, // WRITE START ADDRESS
262 | /* 15 */ (byte) bit,
263 | /* 16 */ 0x00, // WORDS WRITE
264 | /* 17 */ 0x01,
265 | /* 18 */ (byte) (value ? 0x01 : 0x00) // WRITE VALUE
266 | };
267 |
268 | os.write(message, 0, 35);
269 | os.flush();
270 |
271 | int n = readResponse(tmp);
272 | if(n != 30 || tmp[25] != WRITE_ID || tmp[23] != 1) { // check
273 | throw new RuntimeException("write message error");
274 | }
275 |
276 | }
277 |
278 | @Override
279 | public void subscribe(CIO cio) {
280 | synchronized (cioMap) {
281 | if(isConnected()) {
282 | try {
283 | cio.state = read(cio); // initial state
284 | } catch (Exception e) {}
285 | }
286 | cioMap.put(cio.hashCode(), cio);
287 | }
288 | }
289 |
290 | @Override
291 | public boolean unsuscribe(CIO cio) {
292 | synchronized (cioMap) {
293 | return cioMap.remove(cio.hashCode()) != null;
294 | }
295 | }
296 |
297 | @Override
298 | public void subscribe(int position, int bit) {
299 | subscribe(new CIO(position, bit));
300 | }
301 |
302 | @Override
303 | public void unsuscribe(int position, int bit) {
304 | unsuscribe(new CIO(position, bit));
305 | }
306 |
307 | @Override
308 | public void unsuscribeAll() {
309 | synchronized (cioMap) {
310 | cioMap.clear();
311 | }
312 | }
313 |
314 | @Override
315 | public void setInputListener(InputListener listener) {
316 | this.listener = listener;
317 | }
318 |
319 | @Override
320 | public InputListener getInputListener() {
321 | return listener;
322 | }
323 |
324 | private int readResponse(byte[] data) throws IOException {
325 | readFully(is, data, 0, 8);
326 | int n = (data[6] << 1) | (data[7] & 0xFF);
327 | readFully(is, data, 8, n);
328 | return n + 8;
329 | }
330 |
331 | private static void readFully(InputStream is, byte[] data, int off, int len) throws IOException {
332 | int r, n = 0;
333 | while(n < len) {
334 | r = is.read(data, off + n, len - n);
335 | if(r < 0) throw new EOFException();
336 | n += r;
337 | }
338 | }
339 |
340 | public String getHost() {
341 | return host;
342 | }
343 |
344 | public void setHost(String host) {
345 | this.host = host;
346 | }
347 |
348 | public int getPort() {
349 | return port;
350 | }
351 |
352 | public void setPort(int port) {
353 | this.port = port;
354 | }
355 |
356 | public long getRefreshTime() {
357 | return refreshTime;
358 | }
359 |
360 | public void setRefreshTime(long refreshTime) {
361 | this.refreshTime = Math.max(1, refreshTime);
362 | }
363 |
364 | }
365 |
--------------------------------------------------------------------------------
/src/com/delmesoft/cp1l/fins/Hashcode.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | public class Hashcode {
4 |
5 | private int ch, pin;
6 |
7 | public int getCh() {
8 | return ch;
9 | }
10 |
11 | public void setCh(int ch) {
12 | this.ch = ch;
13 | }
14 |
15 | public int getPin() {
16 | return pin;
17 | }
18 |
19 | public void setPin(int pin) {
20 | this.pin = pin;
21 | }
22 |
23 | @Override
24 | public int hashCode() {
25 | final int prime = 31;
26 | int result = 1;
27 | result = prime * result + ch;
28 | result = prime * result + pin;
29 | return result;
30 | }
31 |
32 | @Override
33 | public boolean equals(Object obj) {
34 | if (this == obj)
35 | return true;
36 | if (obj == null)
37 | return false;
38 | if (getClass() != obj.getClass())
39 | return false;
40 | Hashcode other = (Hashcode) obj;
41 | if (ch != other.ch)
42 | return false;
43 | if (pin != other.pin)
44 | return false;
45 | return true;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/com/delmesoft/cp1l/fins/InputListener.java:
--------------------------------------------------------------------------------
1 | package com.delmesoft.cp1l.fins;
2 |
3 | /**
4 | * 2021 Sergio S. - sergi.ss4@gmail.com
5 | * @author Sergio
6 | *
7 | */
8 | public interface InputListener {
9 |
10 | void onEvent(int ch, int pin, boolean state);
11 |
12 | void onError(Exception e);
13 |
14 | }
15 |
--------------------------------------------------------------------------------