@@ -26,66 +26,6 @@ const promiseTimeout = (promise, ms, error = new Error("ASYNC Function Call Time
2626 */
2727const delay = ms => new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
2828
29- /**
30- * Sends a broadcast to a specified IPv4 Interface in order to discover all present EthernetIP
31- * devices. If no interface is specified, checks all available interfaces and sends a broadcast to them.
32- *
33- * @param {string } IPv4Interface - The interface we want to check the PLCs of, if not specified, all interfaces will be checked
34- * @param {function } cb - The callback that is to be executed after the search is finished
35- * @memberof Utilities
36- */
37- function discover ( cb , IPv4Interface = undefined ) {
38- const ENIPList = new Array ( ) ;
39- const IPv4List = new Array ( ) ;
40- /* No specified interface means we need to discover them on our own */
41- if ( IPv4Interface == undefined ) {
42- const interfaceList = os . networkInterfaces ( ) ;
43- const iFaceListKeys = Object . keys ( interfaceList ) ;
44- const iFaceListLen = iFaceListKeys . length ;
45- for ( let i = 0 ; i < iFaceListLen ; i += 1 ) {
46- let interfaces = interfaceList [ iFaceListKeys [ i ] ] ;
47- for ( const addresses of interfaces ) {
48- if ( addresses [ "family" ] == "IPv4" ) {
49- IPv4List . push ( addresses ) ;
50- }
51- }
52- }
53- }
54- /* An interface has been specified! */
55- else {
56- const IPv4RegEx = new RegExp ( "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" ) ;
57- if ( ! IPv4RegEx . test ( IPv4Interface ) ) throw new Error ( "Interface must match IPv4 format!" ) ;
58- IPv4List . push ( { address : IPv4Interface } ) ;
59- }
60- /* For each interface, we send a broadcast with a listIdentity command */
61- for ( const addresses of IPv4List ) {
62- let dsock = dgram . createSocket ( "udp4" ) ;
63- dsock . bind ( 0 , addresses [ "address" ] , ( ) => {
64- dsock . setBroadcast ( true ) ;
65- const { listIdentity } = encapsulation ;
66- dsock . send ( listIdentity ( ) , 44818 , "255.255.255.255" , ( err ) => {
67- if ( err ) throw new Error ( "Error when sending via UDP: " + err ) ;
68- } ) ;
69- } ) ;
70-
71- dsock . on ( "error" , function ( err ) {
72- console . log ( "UDP Error caught: " + err . stack ) ;
73- } ) ;
74-
75- dsock . on ( "message" , function ( msg ) {
76- if ( msg . readUInt16LE ( 0 ) == 0x0063 ) { //Got em! Caught a listIdentity response.
77- const enipPort = msg . readUInt16BE ( 34 ) ;
78- const ipString = msg . readUInt8 ( 36 ) . toString ( ) +
79- "." + msg . readUInt8 ( 37 ) . toString ( ) +
80- "." + msg . readUInt8 ( 38 ) . toString ( ) +
81- "." + msg . readUInt8 ( 39 ) . toString ( ) ;
82- ENIPList . push ( { port :enipPort , ip :ipString } ) ;
83- }
84- } ) ;
85- }
86- setTimeout ( cb , 100 , ENIPList ) ;
87- }
88-
8929/**
9030 * Sends a broadcast to a specified IPv4 Interface in order to discover all present EthernetIP
9131 * devices.
@@ -94,7 +34,7 @@ function discover(cb,IPv4Interface = undefined) {
9434 * @returns {Promise }
9535 * @memberof Utilities
9636 */
97- function getENIPDevicesProm ( ipInterface ) {
37+ function _getENIPDevicesProm ( ipInterface ) {
9838 return new Promise ( ( resolve , reject ) => {
9939 const ENIPList = new Array ( ) ;
10040 let dsock = dgram . createSocket ( "udp4" ) ;
@@ -123,13 +63,44 @@ function getENIPDevicesProm(ipInterface){
12363 } ) ;
12464
12565 dsock . on ( "message" , function ( msg ) {
126- if ( msg . readUInt16LE ( 0 ) == 0x0063 ) { //Got em! Caught a listIdentity response.
127- const enipPort = msg . readUInt16BE ( 34 ) ;
128- const ipString = msg . readUInt8 ( 36 ) . toString ( ) +
129- "." + msg . readUInt8 ( 37 ) . toString ( ) +
130- "." + msg . readUInt8 ( 38 ) . toString ( ) +
131- "." + msg . readUInt8 ( 39 ) . toString ( ) ;
132- ENIPList . push ( { port :enipPort , ip :ipString } ) ;
66+ if ( msg . readUInt16LE ( 0 ) === 0x0063 ) { //Got em! Caught a listIdentity response.
67+ const plcProperties = { } ;
68+ let ptr = 32 ; // Starting with Socket Address
69+ plcProperties . socketAddress = { } ;
70+ plcProperties . socketAddress . sin_family = msg . readUInt16BE ( ptr ) ;
71+ ptr += 2 ;
72+ plcProperties . socketAddress . sin_port = msg . readUInt16BE ( ptr ) ;
73+ ptr += 2 ;
74+ plcProperties . socketAddress . sin_addr = msg . readUInt8 ( ptr ) . toString ( ) +
75+ "." + msg . readUInt8 ( ptr + 1 ) . toString ( ) +
76+ "." + msg . readUInt8 ( ptr + 2 ) . toString ( ) +
77+ "." + msg . readUInt8 ( ptr + 3 ) . toString ( ) ;
78+ ptr += 4 ;
79+ plcProperties . socketAddress . sin_zero = 0 ;
80+ ptr += 8 ;
81+
82+ // Now follows the asset data
83+ plcProperties . vendorID = msg . readUInt16LE ( ptr ) ;
84+ ptr += 2 ;
85+ plcProperties . deviceType = msg . readUInt16LE ( ptr ) ;
86+ ptr += 2 ;
87+ plcProperties . productCode = msg . readUInt16LE ( ptr ) ;
88+ ptr += 2 ;
89+ plcProperties . majorRevision = msg . readUInt8 ( ptr ) ;
90+ ptr += 1 ;
91+ plcProperties . minorRevision = msg . readUInt8 ( ptr ) ;
92+ ptr += 1 ;
93+ plcProperties . status = msg . readUInt16LE ( ptr ) ;
94+ ptr += 2 ;
95+ plcProperties . serialNumber = msg . readUInt32LE ( ptr ) ;
96+ ptr += 4 ;
97+ plcProperties . productNameLength = msg . readUInt8 ( ptr ) ;
98+ ptr += 1 ;
99+ plcProperties . productName = msg . toString ( "ascii" , ptr , msg . length - 1 ) ;
100+ ptr += plcProperties . productNameLength ;
101+ plcProperties . state = msg . readUInt8 ( ptr ) ;
102+
103+ ENIPList . push ( plcProperties ) ;
133104 }
134105 } ) ;
135106 dsock . on ( "close" , function ( ) {
@@ -147,22 +118,22 @@ function getENIPDevicesProm(ipInterface){
147118 * devices. If no interface is specified, checks all available interfaces and sends a broadcast to them.
148119 *
149120 * @param {string } IPv4Interface - The interface we want to check the PLCs of, if not specified, all interfaces will be checked
150- * @returns {Promise }
121+ * @returns {Promise } - an object with a key: interface - value: list of attaches devices pair.
151122 * @memberof Utilities
152123 */
153- async function discoverProm ( IPv4Interface = undefined ) {
154- const ENIPDeviceList = new Array ( ) ;
124+ async function discover ( IPv4Interface = null ) {
125+ const ENIPDevice = { } ;
155126 const IPv4List = new Array ( ) ;
156127
157128 /* No specified interface means we need to discover them on our own */
158- if ( IPv4Interface == undefined ) {
129+ if ( IPv4Interface === null ) {
159130 const interfaceList = os . networkInterfaces ( ) ;
160131 const iFaceListKeys = Object . keys ( interfaceList ) ;
161132 const iFaceListLen = iFaceListKeys . length ;
162133 for ( let i = 0 ; i < iFaceListLen ; i += 1 ) {
163134 let interfaces = interfaceList [ iFaceListKeys [ i ] ] ;
164135 for ( const addresses of interfaces ) {
165- if ( addresses [ "family" ] == "IPv4" ) {
136+ if ( addresses [ "family" ] === "IPv4" ) {
166137 IPv4List . push ( addresses ) ;
167138 }
168139 }
@@ -177,15 +148,15 @@ async function discoverProm(IPv4Interface = undefined) {
177148
178149 /* For each interface, we send a broadcast with a listIdentity command */
179150 for ( const addresses of IPv4List ) {
180- const ENIPDevices = await getENIPDevicesProm ( addresses ) ; // Wait for an Interface to get all Devices
151+ const ENIPDevices = await _getENIPDevicesProm ( addresses ) ; // Wait for an Interface to get all Devices
181152 if ( ! Array . isArray ( ENIPDevices ) || ! ENIPDevices . length ) {
182153 // No Devices returned
183154 }
184155 else {
185- ENIPDeviceList . push ( ENIPDevices ) ;
156+ ENIPDevice [ addresses . address ] = ENIPDevices ;
186157 }
187158 }
188- return ENIPDeviceList ;
159+ return ENIPDevice ;
189160}
190161
191- module . exports = { promiseTimeout, delay, discover, discoverProm } ;
162+ module . exports = { promiseTimeout, delay, discover } ;
0 commit comments