pyctiarbin icon indicating copy to clipboard operation
pyctiarbin copied to clipboard

Feature/jump channel

Open erikamundson opened this issue 1 year ago • 7 comments

Hi all, could use some help here.

Have been attempting to implement the JumpChannel command but am currently stuck getting socket timeout errors (am able to run the already existing commands with no issue).

Error message:

Timeout on receiving message from Arbin!
Traceback (most recent call last):
  File "<fp>\pycti\pyctiarbin\cycler_interface.py", line 132, in _send_receive_msg
    rx_msg += self.__sock.recv(self.__config.msg_buffer_size)
TimeoutError: timed out

Any help or pointers would be appreciated. Thanks!

erikamundson avatar Jan 23 '24 00:01 erikamundson

@erikamundson thank you for submitting the PR. We do not have spare channels at the moment to test the jump_step functionality, but I was able to find an old code that worked at the time:

def jump_step(self, str_step):
        """
        Commands a jump to a specific step type in the schedule.

        Args:
            str_step (str): Any of the keys in schedule_step_idx("rest", "charge", "discharge", "voltage")

        Returns:
            int: 0 if successful otherwise the error code returned by the cycler.
        """
        msg = bytearray([])
        msg += struct.pack('<Q', header)
        msg += struct.pack('<L', 4+4+4+4+101+2)
        msg += struct.pack('<LL', command_codes["SCHEDULE_JUMP"], 0x00000000)

        # Step index
        msg += struct.pack('<L', schedule_step_idx[str_step] - 1)
        # Channel to stop
        msg += struct.pack('<L', self.chan_num - 1)
        msg += bytearray([0x00 for i in range(101)])
        msg += struct.pack('<H', sum(msg))

        resp = self.func_socket(msg)

        idx = 8
        data_len = struct.unpack('<L', resp[idx:idx+4])[0]
        if data_len != 128:
            return -1
        elif data_len != len(resp):
            return -1
        idx += 4
        return_cmd = struct.unpack('<L', resp[idx:idx+4])[0]
        idx += 4+4
        ch_idx = struct.unpack('<L', resp[idx:idx+4])[0]
        idx += 4
        result = struct.unpack('<B', resp[idx:idx+1])[0]
        idx += 1

        if result == 0:
            program_logger.info("Jumped to step {} on channel {}.".format(str_step, self.chan_num))
        elif result == 0x26:
            program_logger.critical("Jumping to step {} on channel {} failed with error code {}: safety check failed.".format(str_step, self.chan_num, result))
        else:
            program_logger.error("Jumping to step {} on channel {} failed with error code {}.".format(str_step, self.chan_num, result))
            if result == 18:
                    program_logger.error("Exiting!.")
                    sys.exit(18)
        return result

This is not class based and is a bit basic, but can potentially be helpful to debug? Please let me know if this works or not and we can try to implement this in class-based version of pycti.

Though, since your error is timeout related, I am not sure if the implementation itself is the culprit. Can you describe your setup a bit? The version of MITS Pro, whether you are running this from the same machine as the cycler computer, etc.

chintanp avatar Jan 27 '24 00:01 chintanp

@chintanp Thanks for the response.

I'm having a hard time testing that code since it looks like it's part of a class that I don't have access to. I did try updating the message length and byte formats to match that function and the timeout still occurs.

Though, since your error is timeout related, I am not sure if the implementation itself is the culprit. Can you describe your setup a bit? The version of MITS Pro, whether you are running this from the same machine as the cycler computer, etc.

We're running MITS Pro version MITS8 202103, which I believe does have the jump command based on the CTI instructions given to us by Arbin (using the C# DLL). I am testing this from the same computer that's running the MITS software.

The code I'm using to test is:

if __name__ == '__main__':
    CHANNEL_INTERFACE_CONFIG = CYCLER_INTERFACE_CONFIG.copy()
    CHANNEL_INTERFACE_CONFIG["channel"] = 25
    channel_interface = ChannelInterface(CHANNEL_INTERFACE_CONFIG)

    print(channel_interface.read_channel_status())
    response = channel_interface.jump_channel(2)
    print(response)

The channel_interface.read_channel_status() works correctly and prints the expected info, but the jump_channel command runs into a timeout (I tried increasing it to 60s and it still times out).

erikamundson avatar Jan 29 '24 18:01 erikamundson

Will try jumping channel this week and get back to you. Just to make sure you are trying to "jump to the next step", correct? Maybe renaming the function will make the intent clearer.

chintanp avatar Jan 30 '24 18:01 chintanp

Will try jumping channel this week and get back to you. Just to make sure you are trying to "jump to the next step", correct? Maybe renaming the function will make the intent clearer.

Yes, it is supposed to jump to the given step ID as defined by the schedule file. I named the function jump_channel since the CTI protocol calls it "THIRD_PARTY_JUMP_CHANNEL", but something like jump_to_step might be a better name.

erikamundson avatar Jan 30 '24 18:01 erikamundson

Hi @chintanp were you able to find some time to test out the jump functionality?

erikamundson avatar Feb 26 '24 17:02 erikamundson

Hi @chintanp were you able to find some time to test out the jump functionality?

@erikamundson a few people working on this are out sick, unfortunately I dont have a expected date on this. Could you tell a little more whey you need the jump functionality? We found this to be rather flaky in our first implementation and therefore didnt implement it this time around.

chintanp avatar Feb 26 '24 20:02 chintanp

@erikamundson a few people working on this are out sick, unfortunately I dont have a expected date on this. Could you tell a little more whey you need the jump functionality? We found this to be rather flaky in our first implementation and therefore didnt implement it this time around.

No worries, there's no big rush for us and we can use the provided C# DLL temporarily if we need to.

Our main use case is remote intervention in case of unsafe conditions. For example, if we detect a short during a charge we would want to jump that channel to a rest step. We'd also like to apply the same rules across different cyclers (eg. Neware, Maccor, Arbin), so writing our business logic into the schedule files is not ideal.

erikamundson avatar Feb 26 '24 21:02 erikamundson