Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Hi6200 Agent for reading LN2 on SATp #555

Merged
merged 19 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added socs/agents/hi6200/.driver.py.swo
mjrand marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
Binary file added socs/agents/hi6200/.drivers.py.swm
Binary file not shown.
Binary file added socs/agents/hi6200/.drivers.py.swn
Binary file not shown.
Binary file added socs/agents/hi6200/.drivers.py.swo
Binary file not shown.
137 changes: 137 additions & 0 deletions socs/agents/hi6200/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import argparse
import time

from ocs import ocs_agent, site_config
from ocs.ocs_twisted import TimeoutLock

from socs.agents.hi6200.drivers import Hi6200Interface


class Hi6200Agent:
mjrand marked this conversation as resolved.
Show resolved Hide resolved
def __init__(self, agent, ip_address, tcp_port):
self.agent = agent
self.log = agent.log
self.lock = TimeoutLock()

self.job = None
self.ip_address = ip_address
self.tcp_port = tcp_port
self.monitor = False

self.scale = None

# Registers Scale Output
agg_params = {
'frame_length': 10 * 60,
}
self.agent.register_feed('scale_output',
record=True,
agg_params=agg_params,
buffer_time=0)

@ocs_agent.param('_')
def init(self, session, params=None):
"""init()

**Task** - Initialize connection to the Hi 6200 Weight Sensor.

"""
with self.lock.acquire_timeout(0) as acquired:
if not acquired:
return False, "Could not acquire lock"

self.scale = Hi6200Interface(self.ip_address, self.tcp_port)

self.log.info("Connected to scale.")

return True, 'Initialized Scale.'

@ocs_agent.param('wait', type=float, default=1)
def monitor_weight(self, session, params=None):
"""
BrianJKoopman marked this conversation as resolved.
Show resolved Hide resolved

**Process** - Continuously monitor scale gross and net weights.

Parameters:
wait (float, optional): Time to wait between measurements
[seconds].

"""
mjrand marked this conversation as resolved.
Show resolved Hide resolved
session.set_status('running')
self.monitor = True

while self.monitor:
with self.lock.acquire_timeout(1) as acquired:
if acquired:
data = {
'timestamp': time.time(),
'block_name': 'weight',
'data': {}
}

try:
data['data']["Gross"] = self.scale.read_scale_gross_weight()
data['data']["Net"] = self.scale.read_scale_net_weight()

self.agent.publish_to_feed('scale_output', data)

# Allow this process to be queried to return current data
session.data = data

except ValueError as e:
self.log.error(f"Scale responded with an anomolous number, ignorning: {e}")

except AttributeError as e:
self.log.error(f"Scale dropped TCP connection momentarily, trying again: {e}")
BrianJKoopman marked this conversation as resolved.
Show resolved Hide resolved

# Allow this process to be queried to return current data
session.data = data

else:
self.log.warn("Could not acquire in monitor_weight")

time.sleep(params['wait'])
BrianJKoopman marked this conversation as resolved.
Show resolved Hide resolved

return True, "Finished monitoring weight"

def stop_monitoring(self, session, params=None):
self.monitor = False
return True, "Stopping current monitor"


def make_parser(parser=None):
"""Build the argument parser for the Agent. Allows sphinx to automatically
build documentation based on this function.

"""
if parser is None:
parser = argparse.ArgumentParser()

# Add options specific to this agent.
pgroup = parser.add_argument_group('Agent Options')
pgroup.add_argument('--ip-address')
pgroup.add_argument('--tcp-port')

return parser


def main(args=None):

parser = make_parser()
args = site_config.parse_args(agent_class='Hi6200Agent',
parser=parser,
args=args)

agent, runner = ocs_agent.init_site_agent(args)

p = Hi6200Agent(agent, args.ip_address, int(args.tcp_port))

agent.register_task('init', p.init)

agent.register_process('monitor_weight', p.monitor_weight, p.stop_monitoring)

runner.run(agent, auto_reconnect=True)


if __name__ == '__main__':
main()
75 changes: 75 additions & 0 deletions socs/agents/hi6200/drivers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import struct

from pyModbusTCP.client import ModbusClient


class Hi6200Interface():

"""
The Hi6200 Weight Processor uses a Modbus TCP Interface to communicate.
The Gross and Net weight sensors are always available to read on the 8,9 and 6,7 registers respectively.
"""

def __init__(self, ip_address, tcp_port, verbose=False, **kwargs):
"""
Connects to the Hi6200 weight sensor using a TCP Modbus Client with pyModbusTCP.
This works ~similarly to a socket connection using a IP and port.
ModbusClient will not throw errors upon incorrect ip_address!
ModbusClient will also allow multiple recconects unlike a socket.
mjrand marked this conversation as resolved.
Show resolved Hide resolved
mjrand marked this conversation as resolved.
Show resolved Hide resolved
"""

self.scale = ModbusClient(host=ip_address, port=tcp_port, auto_open=True, auto_close=False)

def read_scale_gross_weight(self):
"""
Returns the current gross weight reading of the scale in the sensors chosen unit (kg)
"""

# The gross weight is always available on the 8,9 registers.
# Reading these registers will return an int.
a, b = self.scale.read_holding_registers(8, 2)

# The ints read on the registers must be converted to hex.
# The hex bits are then concatenated and converted to float as CDAB

# Strip the '0x' hex bit prefix as it is not useful.
# Then concatenate the bits
hex_a = hex(a)[2:]
while len(hex_a) < 4:
hex_a = '0' + hex_a
mjrand marked this conversation as resolved.
Show resolved Hide resolved

hex_b = hex(b)[2:]
while len(hex_b) < 4:
hex_b = '0' + hex_b

hex_weight = hex_b + hex_a

# This struct function converts the concatenated hex bits to a float.
return struct.unpack('!f', bytes.fromhex(hex_weight))[0]
mjrand marked this conversation as resolved.
Show resolved Hide resolved

def read_scale_net_weight(self):
"""
Returns the current net weight reading of the scale in the sensors chosen unit (kg)
"""

# The gross weight is always available on the 6,7 registers.
# Reading these registers will return an int.
a, b = self.scale.read_holding_registers(6, 2)

# The ints read on the registers must be converted to hex.
# The hex bits are then concatenated and converted to float as CDAB.

# Strip the '0x' hex bit prefix as it is not useful.
# Then concatenate the bits
hex_a = hex(a)[2:]
while len(hex_a) < 4:
hex_a = '0' + hex_a

hex_b = hex(b)[2:]
while len(hex_b) < 4:
hex_b = '0' + hex_b

hex_weight = hex_b + hex_a

# This struct function converts the concatenated hex bits to a float.
return struct.unpack('!f', bytes.fromhex(hex_weight))[0]
1 change: 1 addition & 0 deletions socs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
'FlowmeterAgent': {'module': 'socs.agents.ifm_sbn246_flowmeter.agent', 'entry_point': 'main'},
'FTSAerotechAgent': {'module': 'socs.agents.fts_aerotech.agent', 'entry_point': 'main'},
'GeneratorAgent': {'module': 'socs.agents.generator.agent', 'entry_point': 'main'},
'Hi6200Agent': {'module': 'socs.agents.hi6200.agent', 'entry_point': 'main'},
'HWPBBBAgent': {'module': 'socs.agents.hwp_encoder.agent', 'entry_point': 'main'},
'HWPGripperAgent': {'module': 'socs.agents.hwp_gripper.agent', 'entry_point': 'main'},
'HWPPicoscopeAgent': {'module': 'socs.agents.hwp_picoscope.agent', 'entry_point': 'main'},
Expand Down