jisotalo / ads-client

Unofficial Node.js ADS library for connecting to Beckhoff TwinCAT automation systems using ADS protocol.
https://jisotalo.fi/ads-client/
MIT License
80 stars 17 forks source link

Issue reading structs containing BIT datatype #100

Closed isaac-nls closed 3 months ago

isaac-nls commented 1 year ago

Hi,

I'm having an issue where I cannot subscribe to structures containing BITs. I can subscribe to everything else just fine.

For example, I cannot subscribe to the struct below:

TYPE ST_SYSTEM_STATE :
STRUCT
    //all subsystems
    bStateMachineRunning                    : BIT; 
    bMotorsPowered                      : BIT;  
    bInitComplete                           : BIT;  
    bIsHoming                           : BIT;  
    bHasHomed                           : BIT;  
    bIsReady                                : BIT;  
    bIsBusy                             : BIT; 
    bMotorsMoving                       : BIT;  
    bHasStopped                         : BIT; 
    bHasPaused                          : BIT;
END_STRUCT
END_TYPE

Turning on debugging in ads-client, I receive the following message:

at async \node_modules\ads-client\src\ads-client.js:6325:35 { code: 'ERR_BUFFER_OUT_OF_BOUNDS' } +4ms
  ads-client:details IO in  <------ 68 bytes +494ms
  ads-client:details _parseAmsTcpHeader(): Starting to parse AMS/TCP header +1ms
  ads-client:details _parseAmsTcpHeader(): AMS/TCP header parsed: { command: 0, commandStr: 'AMS_TCP_PORT_AMS_CMD', dataLength: 62 } +0ms
  ads-client:details _parseAmsHeader(): Starting to parse AMS header +1ms
  ads-client:details _parseAmsHeader(): AMS header parsed: { targetAmsNetId: '192.168.200.1.1.1', targetAdsPort: 33242, sourceAmsNetId: '192.168.11.2.1.1', sourceAdsPort: 851, adsCommand: 8, adsCommandStr: 'Notification', stateFlags: 4, stateFlagsStr: 'AdsCommand, Tcp, Request', dataLength: 30, errorCode: 0, invokeId: 0, error: false, errorStr:
  ads-client:details _getDataTypeInfo(): Data type requested for BIT +0ms
  ads-client:details _getDataTypeInfo(): Data type info found from cache for BIT +0ms
  ads-client:details _getDataTypeInfo(): Data type requested for BIT +0ms  ads-client:details _getDataTypeInfo(): Data type info found from cache for BIT +1ms
  ads-client _onAdsCommandReceived(): Ads notification received but parsing Javascript object failed: RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds

Any help would be greatly appreciated.

Thanks! Isaac

jisotalo commented 1 year ago

Hi!

Actually the BIT data type is not available/working at the moment. I thought that I had it in README but I didn't.. Basically it is in the code however handled like a BOOL and it would require some extra effort to be handled correctly.

So I would suggest to use BOOL instead if possible, or just a single WORD and then get the bits yourself. Personally I never use BITs with TwinCAT as the support is not that good.

Edit: I will keep this issue open as this certainly should work some day if it's technically doable.

isaac-nls commented 1 year ago

Hi jisotalo,

Thanks very much for the quick reply. Good to know I wasn't going crazy! We initially were using BITs instead of BOOLs to reduce the size of the overall packets over the network, but it's not a big deal for us to change over to using BOOLs instead. I'll keep it in mind for future development not to use BITs.

Once again, I very much appreciate the time and effort you've put into this library.

Thanks! Isaac

jisotalo commented 1 year ago

If you really need to read struct with BITs it's possible but needs a little work. I tested the following and it works fine!

PLC side

TYPE ST_Bits:
STRUCT
  Bit_0 : BIT;
  Bit_1 : BIT;
  Bit_2 : BIT;
  Bit_3 : BIT;
  Bit_4 : BIT;
  Bit_5 : BIT;
  Bit_6 : BIT;
  Bit_7 : BIT;
END_STRUCT
END_TYPE
//GVL_BitTest
{attribute 'qualified_only'}
VAR_GLOBAL
  Bits : ST_Bits;
END_VAR

Node.js side

const ads = require('ads-client');
const client = new ads.Client({
  targetAmsNetId: 'localhost',
  targetAdsPort: 851
});

client.connect()
  .then(async res => {   
    console.log(`Connected to the ${res.targetAmsNetId}`);
    console.log(`Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort}`);

    //First getting the symbol info for this variable
    const info = await client.getSymbolInfo('GVL_BitTest.Bits');

    //Using symbol info to subscribe to raw data
    const sub = await client.subscribeRaw(info.indexGroup, info.indexOffset, info.size, (data, sub) => {
      //Converting Buffer to byte
      const value = data.value.readUint8();

      const converted = {
        bit_0: !!(value & (0x01 << 0)),
        bit_1: !!(value & (0x01 << 1)),
        bit_2: !!(value & (0x01 << 2)),
        bit_3: !!(value & (0x01 << 3)),
        bit_4: !!(value & (0x01 << 4)),
        bit_5: !!(value & (0x01 << 5)),
        bit_6: !!(value & (0x01 << 6)),
        bit_7: !!(value & (0x01 << 7)),
      }
      console.log(converted);
      /* Should print something like:
      {
        bit_0: true,
        bit_1: false,
        bit_2: true,
        bit_3: false,
        bit_4: true,
        bit_5: false,
        bit_6: true,
        bit_7: false
      }
      */
    });
  })
  .catch(err => {
    console.log('Something failed:', err);
  })

Thanks again and have a nice christmas!

jisotalo commented 4 months ago

Adding this to version 2.0 milestone. Let's see if it's possible easily or not.

jisotalo commented 3 months ago

Development ongoing, reading already works in commit https://github.com/jisotalo/ads-client/commit/f6a372464d1afdf8af1189ca2c122b26f035cfb6

jisotalo commented 3 months ago

Reading and writing BITs now works in v2 (at least in my tests) - commit https://github.com/jisotalo/ads-client/commit/c3507e2fc1cf73be190e500db167c9dc335e9700