diff --git a/cfg/ls240.cfg b/cfg/ls240.cfg new file mode 100644 index 0000000..c61fb85 --- /dev/null +++ b/cfg/ls240.cfg @@ -0,0 +1,17 @@ +[NODE] +id = ls240.psi.ch +description = ls240 test + +[INTERFACE] +uri = tcp://5000 + +[T] +description = temperature on uniax stick +class = secop_psi.ls240.Ls240 +iodev = T_iodev + +[T_iodev] +class = secop.bytesio.BytesIO +description = IO device for LS240 +uri = serial:///dev/ttyUSB0?baudrate=9600+parity=EVEN +timeout = 0.2 diff --git a/cfg/uniax.cfg b/cfg/uniax.cfg index 4e9916f..99689b7 100644 --- a/cfg/uniax.cfg +++ b/cfg/uniax.cfg @@ -8,25 +8,39 @@ uri = tcp://5000 [drv_iodev] description = class = secop.bytesio.BytesIO -uri = serial:///dev/ttyUSB0?baudrate=57600 -# uri = serial:///dev/ttyUSB0?baudrate=9600 +# uri = serial:///dev/ttyUSB1?baudrate=57600 +uri = tcp://192.168.127.254:3002 [drv] description = trinamic motor test class = secop_psi.trinamic.Motor iodev = drv_iodev standby_current=0.1 -maxcurrent=1.4 -acceleration=50 -maxspeed=200 -zero=-36 -enc_tolerance=3.6 -free_wheeling=0.001 -pull_up=1 +maxcurrent=0.2 +acceleration=150. +movelimit=360 +speed=40 +encoder_tolerance=3.6 +free_wheeling=0.1 +power_down_delay=0.1 [force] description = DPM driver to read out the transducer value, write and read the offset and scale factor class = secop_psi.dpm.DPM3 -uri = serial:///dev/ttyUSB1?baudrate=9600 +#uri = serial:///dev/ttyUSB0?baudrate=9600 +uri = tcp://192.168.127.254:3001 digits = 2 scale_factor = 0.0156 +motor = drv + +[res] +description = temperature on uniax stick +class = secop_psi.ls340res.ResChannel +uri = tcp://192.168.127.254:3003 +channel = C + +[T] +class = secop_psi.softcal.Sensor +rawsensor = res +calib = /home/l_samenv/frappy/secop_psi/calcurves/X132254.340 +value.unit = K diff --git a/secop/core.py b/secop/core.py index 8a5d2ae..b2a8a7b 100644 --- a/secop/core.py +++ b/secop/core.py @@ -37,4 +37,5 @@ from secop.poller import AUTO, DYNAMIC, REGULAR, SLOW from secop.properties import Property from secop.proxy import Proxy, SecNode, proxy_class from secop.stringio import HasIodev, StringIO +from secop.bytesio import BytesIO from secop.persistent import PersistentMixin, PersistentParam diff --git a/secop/lib/asynconn.py b/secop/lib/asynconn.py index e19feb4..1ea32d0 100644 --- a/secop/lib/asynconn.py +++ b/secop/lib/asynconn.py @@ -251,7 +251,9 @@ class AsynSerial(AsynConn): if not fullname.startswith(name): raise ConfigError('illegal parity: %s' % parity) options['parity'] = name[0] - if 'timeout' not in options: + if 'timeout' in options: + options['timeout'] = float(self.timeout) + else: options['timeout'] = self.timeout try: self.connection = Serial(dev, **options) diff --git a/secop/persistent.py b/secop/persistent.py index 352debd..6f3f3a4 100644 --- a/secop/persistent.py +++ b/secop/persistent.py @@ -92,7 +92,7 @@ class PersistentMixin(HasAccessibles): try: with open(self.persistentFile, 'r') as f: self.persistentData = json.load(f) - except FileNotFoundError: + except Exception: self.persistentData = {} writeDict = {} for pname in self.parameters: diff --git a/secop_psi/calcurves/X132254.340 b/secop_psi/calcurves/X132254.340 new file mode 100644 index 0000000..b3591b4 --- /dev/null +++ b/secop_psi/calcurves/X132254.340 @@ -0,0 +1,208 @@ +Sensor Model: CX-1050-SD 2018-11-20 +Serial Number: X132254 +Data Format: 4 (Log Ohms/Kelvin) +SetPoint Limit: 330.0 (Kelvin) +Temperature coefficient: 1 (Negative) +Number of Breakpoints: 199 + +No. Units Temperature (K) + + 1 1.865581 330.0000 + 2 1.889780 310.0000 + 3 1.900613 301.5330 + 4 1.911510 293.2973 + 5 1.922466 285.2865 + 6 1.933476 277.4945 + 7 1.944536 269.9153 + 8 1.955638 262.5431 + 9 1.966779 255.3723 + 10 1.977950 248.3974 + 11 1.989148 241.6129 + 12 2.000369 235.0138 + 13 2.011610 228.5949 + 14 2.022870 222.3513 + 15 2.034145 216.2782 + 16 2.045433 210.3710 + 17 2.056730 204.6252 + 18 2.068035 199.0363 + 19 2.079343 193.6000 + 20 2.090652 188.3123 + 21 2.101960 183.1689 + 22 2.113263 178.1660 + 23 2.124559 173.2998 + 24 2.135845 168.5665 + 25 2.147117 163.9624 + 26 2.158374 159.4842 + 27 2.169612 155.1282 + 28 2.180830 150.8912 + 29 2.192023 146.7699 + 30 2.203192 142.7612 + 31 2.214333 138.8620 + 32 2.225444 135.0693 + 33 2.236525 131.3801 + 34 2.247573 127.7918 + 35 2.258586 124.3014 + 36 2.269564 120.9064 + 37 2.280509 117.6041 + 38 2.291423 114.3920 + 39 2.302308 111.2676 + 40 2.313167 108.2285 + 41 2.324003 105.2725 + 42 2.334817 102.3972 + 43 2.345609 99.60045 + 44 2.356380 96.88007 + 45 2.367129 94.23400 + 46 2.377857 91.66019 + 47 2.388562 89.15669 + 48 2.399245 86.72156 + 49 2.409907 84.35294 + 50 2.420547 82.04902 + 51 2.431166 79.80803 + 52 2.441762 77.62824 + 53 2.452338 75.50799 + 54 2.462894 73.44565 + 55 2.473431 71.43963 + 56 2.483949 69.48841 + 57 2.494449 67.59048 + 58 2.504932 65.74439 + 59 2.515397 63.94872 + 60 2.525846 62.20210 + 61 2.536278 60.50318 + 62 2.546695 58.85066 + 63 2.557096 57.24328 + 64 2.567482 55.67980 + 65 2.577854 54.15902 + 66 2.588212 52.67978 + 67 2.598555 51.24095 + 68 2.608882 49.84141 + 69 2.619195 48.48010 + 70 2.629492 47.15596 + 71 2.639773 45.86800 + 72 2.650037 44.61521 + 73 2.660285 43.39664 + 74 2.670517 42.21135 + 75 2.680731 41.05844 + 76 2.690928 39.93701 + 77 2.701107 38.84622 + 78 2.711268 37.78522 + 79 2.721411 36.75319 + 80 2.731536 35.74936 + 81 2.741644 34.77294 + 82 2.751735 33.82319 + 83 2.761808 32.89938 + 84 2.771865 32.00080 + 85 2.781907 31.12677 + 86 2.791933 30.27661 + 87 2.801945 29.44966 + 88 2.811944 28.64531 + 89 2.821930 27.86292 + 90 2.831903 27.10191 + 91 2.841865 26.36167 + 92 2.851817 25.64166 + 93 2.861758 24.94131 + 94 2.871690 24.26009 + 95 2.881614 23.59748 + 96 2.891532 22.95297 + 97 2.901446 22.32605 + 98 2.911361 21.71626 + 99 2.921277 21.12313 +100 2.931198 20.54620 +101 2.941127 19.98502 +102 2.951066 19.43917 +103 2.961018 18.90823 +104 2.970985 18.39179 +105 2.980970 17.88946 +106 2.990974 17.40085 +107 3.001002 16.92558 +108 3.011054 16.46329 +109 3.021133 16.01363 +110 3.031243 15.57626 +111 3.041384 15.15082 +112 3.051561 14.73701 +113 3.061776 14.33450 +114 3.072032 13.94298 +115 3.082331 13.56216 +116 3.092677 13.19174 +117 3.103073 12.83143 +118 3.113522 12.48097 +119 3.124027 12.14008 +120 3.134592 11.80850 +121 3.145221 11.48597 +122 3.155918 11.17226 +123 3.166687 10.86711 +124 3.177532 10.57030 +125 3.188458 10.28159 +126 3.199469 10.00077 +127 3.210570 9.727624 +128 3.221764 9.461935 +129 3.233053 9.203502 +130 3.244440 8.952128 +131 3.255927 8.707619 +132 3.267518 8.469789 +133 3.279216 8.238455 +134 3.291024 8.013439 +135 3.302945 7.794568 +136 3.314984 7.581676 +137 3.327143 7.374599 +138 3.339428 7.173177 +139 3.351843 6.977257 +140 3.364390 6.786688 +141 3.377076 6.601324 +142 3.389904 6.421023 +143 3.402879 6.245646 +144 3.416005 6.075059 +145 3.429288 5.909132 +146 3.442733 5.747736 +147 3.456347 5.590749 +148 3.470134 5.438050 +149 3.484101 5.289521 +150 3.498250 5.145049 +151 3.512586 5.004522 +152 3.527113 4.867834 +153 3.541835 4.734880 +154 3.556757 4.605557 +155 3.571882 4.479766 +156 3.587215 4.357410 +157 3.602759 4.238397 +158 3.618520 4.122634 +159 3.634504 4.010033 +160 3.650715 3.900507 +161 3.667161 3.793973 +162 3.683849 3.690349 +163 3.700786 3.589555 +164 3.717980 3.491514 +165 3.735437 3.396150 +166 3.753166 3.303392 +167 3.771176 3.213166 +168 3.789476 3.125406 +169 3.808071 3.040042 +170 3.826971 2.957009 +171 3.846182 2.876245 +172 3.865712 2.797686 +173 3.885567 2.721273 +174 3.905755 2.646948 +175 3.926284 2.574652 +176 3.947160 2.504331 +177 3.968390 2.435930 +178 3.989980 2.369398 +179 4.011939 2.304683 +180 4.034269 2.241735 +181 4.056978 2.180507 +182 4.080072 2.120951 +183 4.103563 2.063022 +184 4.127465 2.006675 +185 4.151790 1.951866 +186 4.176551 1.898555 +187 4.201764 1.846700 +188 4.227442 1.796262 +189 4.253600 1.747201 +190 4.280254 1.699479 +191 4.307419 1.653062 +192 4.335111 1.607912 +193 4.363347 1.563995 +194 4.392142 1.521278 +195 4.421513 1.479727 +196 4.451476 1.439312 +197 4.482048 1.400000 +198 4.664046 1.200000 +199 4.907708 1.000000 diff --git a/secop_psi/dpm.py b/secop_psi/dpm.py index 722b86d..6cb57d6 100644 --- a/secop_psi/dpm.py +++ b/secop_psi/dpm.py @@ -16,11 +16,13 @@ # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Module authors: -# ... +# M. Zolliker # # ***************************************************************************** +"""transducer DPM3 read out""" -from secop.core import Readable, Parameter, FloatRange, BoolType, StringIO, HasIodev, IntRange, Done +from secop.core import Drivable, Parameter, FloatRange, BoolType, StringIO,\ + HasIodev, IntRange, Done, Attached, Command class DPM3IO(StringIO): @@ -43,7 +45,7 @@ def float2hex(value, digits): return '%06X' % intvalue -class DPM3(HasIodev, Readable): +class DPM3(HasIodev, Drivable): OFFSET = 0x8f SCALE = 0x8c @@ -52,8 +54,11 @@ class DPM3(HasIodev, Readable): iodevClass = DPM3IO + motor = Attached() digits = Parameter('number of digits for value', IntRange(0, 5), initwrite=True, readonly=False) value = Parameter(unit='N') + target = Parameter(unit='N') + step = Parameter('maximum motor step', FloatRange(unit='deg'), default=5, readonly=False) offset = Parameter('', FloatRange(-1e5, 1e5), readonly=False, poll=True) @@ -62,6 +67,8 @@ class DPM3(HasIodev, Readable): thus a maximl output of 1500. 10=150/f """ scale_factor = Parameter('', FloatRange(-1e5, 1e5, unit='input_units/N'), readonly=False, poll=True) + _target = None + fast_pollfactor = 0.01 def query(self, adr, value=None): if value is not None: @@ -93,7 +100,7 @@ class DPM3(HasIodev, Readable): return value/mag else: return hex2float(hexvalue, self.digits) - + def write_digits(self, value): # value defines the number of digits back_value=self._iodev.communicate('*1F135%02X\r*1G135' % (value + 1)) @@ -108,9 +115,37 @@ class DPM3(HasIodev, Readable): return int(back_value,16) - 1 def read_value(self): - value = self._iodev.communicate('*1B1') - return float(value) + value = float(self._iodev.communicate('*1B1')) + if self._target is not None: + mot = self._motor + if self._direction * (self._target - value) > 0: + if not mot.isBusy(): + step = self.step * self._direction + mot.write_target(mot.value + step) + else: + print(value) + self.stop() + self.status = self.Status.IDLE, 'target reached' + return value + def write_target(self, target): + self._target = target + if target - self.value > 0: + self._direction = 1 + else: + self._direction = -1 + print('direction', self._direction) + self.status = self.Status.BUSY, 'moving motor' + if self._motor.status[0] == self.Status.ERROR: + self._motor.reset() + return target + + @Command() + def stop(self): + self._target = None + self._motor.stop() + self.status = self.Status.IDLE, 'stopped' + def read_offset(self): reply = self.query(self.OFFSET) return reply diff --git a/secop_psi/ls240.py b/secop_psi/ls240.py new file mode 100644 index 0000000..65d7850 --- /dev/null +++ b/secop_psi/ls240.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ***************************************************************************** +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Module authors: +# Markus Zolliker +# ***************************************************************************** +"""LakeShore 240 temperature monitor""" + +import struct +from secop.core import FloatRange, HasIodev, Readable, Parameter, BytesIO +from secop.errors import CommunicationFailedError + +SD1= 0x10 +SD2= 0x68 +FC = 0x49 +ED = 0x16 + +STATUS_REQ = 0x49 + +def dehex(msg): + return bytes(int(b, 16) for b in msg.split()) + +def hexify(msg): + return ' '.join('%2.2X' % b for b in msg) + + +class Ls240(HasIodev, Readable): + value = Parameter('sensor reading', FloatRange(unit='Ohm')) + + iodevClass = BytesIO + + def request(self, replylen, data='', ext=None, dst_adr=3, src_adr=2): + data = dehex(data) + if ext is None: + ext = len(data) > 1 + if ext: + dst_adr |= 0x80 + src_adr |= 0x80 + if len(data) > 1: + length = len(data) + 2 + hdr = [SD2, length, length, SD2] + else: + hdr = [SD1] + mid = [dst_adr, src_adr] + list(data) + checksum = sum(mid) % 256 + msg = bytes(hdr + mid + [checksum, ED]) + for i in range(10): + try: + # print('>', hexify(msg)) + reply = self._iodev.communicate(msg, replylen) + # print('<', hexify(reply)) + except (TimeoutError, CommunicationFailedError): + continue + return reply + return None + + def read_value(self): + + # check connection + self.request(6, '49') + + # get diag + # 3C: slave diag, (what means 3E?) + reply = self.request(17, '6D 3C 3E') + assert reply[13:15] == b'\x0f\x84' # LS240 ident + + # set parameters + # 3D set param (what means 3E?) + # B0 FF FF: no watchdog, 00: min wait, 0F 84: ident, 01: group + assert b'\xe5' == self.request(1, '5D 3D 3E B0 FF FF 00 0F 84 01') + + # set config + # 3E set config (what means 2nd 3E?) + # 93: input only, 4 == 3+1 bytes + assert b'\xe5' == self.request(1, '7D 3E 3E 93') + + # get diag + # 3C: slave diag, (what means 3E?) + reply = self.request(17, '5D 3C 3E') + assert reply[13:15] == b'\x0f\x84' # LS240 ident + + # get data + # do not know what 42 24 means + reply = self.request(13, '7D 42 24', ext=0) + print('DATA', reply) + value = struct.unpack('>f', reply[7:11])[0] + print('VALUE', value) + return value diff --git a/secop_psi/ls340res.py b/secop_psi/ls340res.py new file mode 100644 index 0000000..a8f3f7c --- /dev/null +++ b/secop_psi/ls340res.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ***************************************************************************** +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Module authors: +# Markus Zolliker +# ***************************************************************************** +"""very simple LakeShore Model 340 driver, resistivity only""" + +import time + +from secop.datatypes import StringType, FloatRange +from secop.modules import Parameter, Property, Readable +from secop.stringio import HasIodev, StringIO + + +class LscIO(StringIO): + identification = [('*IDN?', 'LSCI,MODEL340,.*')] + end_of_line = '\r' + wait_before = 0.05 + + +class ResChannel(HasIodev, Readable): + """temperature channel on Lakeshore 340""" + + iodevClass = LscIO + + value = Parameter(datatype=FloatRange(unit='Ohm')) + channel = Property('the channel A,B,C or D', StringType()) + + def read_value(self): + return self._iodev.communicate('SRDG?%s' % self.channel) diff --git a/secop_psi/softcal.py b/secop_psi/softcal.py index 927da18..e1ddf3a 100644 --- a/secop_psi/softcal.py +++ b/secop_psi/softcal.py @@ -198,7 +198,7 @@ class Sensor(Readable): def update_value(self, value): if self.abs: - value = abs(value) + value = abs(float(value)) self.value = self._calib(value) self._value_error = None diff --git a/secop_psi/trinamic.py b/secop_psi/trinamic.py index a0ad2ef..67c9aa2 100644 --- a/secop_psi/trinamic.py +++ b/secop_psi/trinamic.py @@ -97,7 +97,7 @@ class Motor(PersistentMixin, HasIodev, Drivable): FloatRange(0, 360., unit='$'), 212, ANGLE_SCALE, readonly=False, group='more') speed = HwParam('max. speed', FloatRange(0, MAX_SPEED, unit='$/sec'), - 4, SPEED_SCALE, readonly=False, group='more') + 4, SPEED_SCALE, readonly=False, group='motorparam') minspeed = HwParam('min. speed', FloatRange(0, MAX_SPEED, unit='$/sec'), 130, SPEED_SCALE, readonly=False, default=SPEED_SCALE, group='motorparam') currentspeed = HwParam('current speed', FloatRange(-MAX_SPEED, MAX_SPEED, unit='$/sec'), @@ -221,7 +221,6 @@ class Motor(PersistentMixin, HasIodev, Drivable): initialized = self.comm(GET_GLOB_PAR, 255, bank=2) if initialized: # no power loss self.saveParameters() - print('SAVED', self.persistentData) else: # just powered up # get persistent values writeDict = self.loadParameters() @@ -403,18 +402,18 @@ class Motor(PersistentMixin, HasIodev, Drivable): self.status = self.Status.IDLE, 'stopped' self._started = 0 - @Command() - def step(self): - self.comm(MOVE, 1, FULL_STEP / ANGLE_SCALE) + #@Command() + #def step(self): + # self.comm(MOVE, 1, FULL_STEP / ANGLE_SCALE) - @Command() - def back(self): - self.comm(MOVE, 1, - FULL_STEP / ANGLE_SCALE) + #@Command() + #def back(self): + # self.comm(MOVE, 1, - FULL_STEP / ANGLE_SCALE) - @Command(IntRange(), result=IntRange()) - def get_axis_par(self, adr): - return self.comm(GET_AXIS_PAR, adr) + #@Command(IntRange(), result=IntRange()) + #def get_axis_par(self, adr): + # return self.comm(GET_AXIS_PAR, adr) - @Command((IntRange(), FloatRange()), result=IntRange()) - def set_axis_par(self, adr, value): - return self.comm(SET_AXIS_PAR, adr, value) + #@Command((IntRange(), FloatRange()), result=IntRange()) + #def set_axis_par(self, adr, value): + # return self.comm(SET_AXIS_PAR, adr, value)