Python Forum
Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
AttributeError
#1
Hi,

I am Mark and i have just registered to this forum and installed Python 3.8.
Im not experienced yet with Python so i like to ask for a possible solution to my questions.

I have a Campbell Scientific CR1000 datalogger which id like to connect to and get data from with Python.
I found a python package called pycampbellcr1000 which ive installed.
Unfortunately i get a AttributeError when i try to connect with the cr1000.
The actual error code is:
Error:
Traceback (most recent call last): File "C:\Program Files\Python\Lib\site-packages\pycampbellcr1000\compat.py", line 94, in <module> stdout = sys.stdout.buffer AttributeError: 'StdOutputFile' object has no attribute 'buffer' >>>
The compat.py file is:
# coding: utf8
'''
    PyCampbellCR1000.compat
    -----------------------

    Workarounds for compatibility with Python 2 and 3 in the same code base.

    :copyright: Copyright 2012 Salem Harrache and contributors, see AUTHORS.
    :license: GNU GPL v3.

'''
import sys

# -------
# Pythons
# -------

# Syntax sugar.
_ver = sys.version_info

#: Python 2.x?
is_py2 = (_ver[0] == 2)

#: Python 3.x?
is_py3 = (_ver[0] == 3)

#: Python 3.0.x
is_py30 = (is_py3 and _ver[1] == 0)

#: Python 3.1.x
is_py31 = (is_py3 and _ver[1] == 1)

#: Python 3.2.x
is_py32 = (is_py3 and _ver[1] == 2)

#: Python 3.3.x
is_py33 = (is_py3 and _ver[1] == 3)

#: Python 3.4.x
is_py34 = (is_py3 and _ver[1] == 4)

#: Python 2.7.x
is_py27 = (is_py2 and _ver[1] == 7)

#: Python 2.6.x
is_py26 = (is_py2 and _ver[1] == 6)

# ---------
# Specifics
# ---------

if is_py2:
    if is_py26:
        from logging import Handler

        class NullHandler(Handler):
            def emit(self, record):
                pass
        from ordereddict import OrderedDict
    else:
        from logging import NullHandler
        from collections import OrderedDict
    from StringIO import StringIO

    ord = ord
    chr = chr

    def to_char(string):
        if len(string) == 0:
            return bytes('')
        return bytes(string[0])

    bytes = str
    str = unicode
    stdout = sys.stdout
    xrange = xrange


elif is_py3:
    from collections import OrderedDict
    from logging import NullHandler
    from io import StringIO

    ord = lambda x: x
    chr = lambda x: bytes([x])

    def to_char(string):
        if len(string) == 0:
            return str('')
        return str(string[0])

    str = str
    bytes = bytes
    stdout = sys.stdout.buffer
    xrange = range


def is_text(data):
    '''Check if data is text instance'''
    return isinstance(data, str)


def is_bytes(data):
    '''Check if data is bytes instance'''
    return isinstance(data, bytes)
Maybe someone over here can provide me a solution for this?

Thanks.

With kind regards,

Mark
Reply
#2
stdout = sys.stdout.buffer
won't work
use print(buffer)
Reply
#3
Hi Larz60+

Thanks alot for your reply.
When i remove the stdout = sys.stdout.buffer and replace that space with print(buffer),
i get a message NameError: name 'buffer' is not defined.

With kind regards,

Mark
Reply
#4
Im using this piece of code to connect to the cr1000 datalogger.
It connect ok and shows time ok, but when it must show the datatables on the cr1000 of give an error.
from pycampbellcr1000 import CR1000
import datetime
device = CR1000.from_url('Serial:COM3:38400')

print("Device is connected: %s" %device.connected)
print(device.gettime())
print(device.list_tables())
The pycampbellcr1000 Package has numerous py files which include the pakbus.py shown below:
# -*- coding: utf-8 -*-
'''
    PyCampbellCR1000.pakbus
    -----------------------

    PakBus protocol Implementation.

    Original Author: Dietrich Feist, Max Planck Institute for Biogeochemistry,
                     Jena Germany (PyPak)
    :copyright: Copyright 2012 Salem Harrache and contributors, see AUTHORS.
    :license: GNU GPL v3.

'''
from __future__ import division, unicode_literals

import struct
import time

from .compat import ord, chr, is_text, is_py3, bytes
from .logger import LOGGER
from .utils import Singleton
from .exceptions import DeliveryFailureException
from .utils import bytes_to_hex, nsec_to_time


class Transaction(Singleton):
    id = 0

    def next_id(self):
        self.id += 1
        self.id &= 0xFF
        return self.id


class PakBus(object):
    '''Interface for a pakbus client.

    :param link: A `PyLink` connection.
    :param dest_addr: Destination physical address (12-bit int) (default dest)
    :param dest: Destination node ID (12-bit int) (default 0x001)
    :param src_addr: Source physical address (12-bit int) (default src)
    :param src: Source node ID (12-bit int) (default 0x802)
    :param security_code: 16-bit security code (default 0x0000)
    '''

    DATATYPE = {
        'Byte': {'code': 1, 'fmt': 'B', 'size': 1},
        'UInt2': {'code': 2, 'fmt': '>H', 'size': 2},
        'UInt4': {'code': 3, 'fmt': '>L', 'size': 4},
        'Int1': {'code': 4, 'fmt': 'b', 'size': 1},
        'Int2': {'code': 5, 'fmt': '>h', 'size': 2},
        'Int4': {'code': 6, 'fmt': '>l', 'size': 4},
        'FP2': {'code': 7, 'fmt': '>H', 'size': 2},
        'FP3': {'code': 15, 'fmt': '3c', 'size': 3},
        'FP4': {'code': 8, 'fmt': '4c', 'size': 4},
        'IEEE4B': {'code': 9, 'fmt': '>f', 'size': 4},
        'IEEE8B': {'code': 18, 'fmt': '>d', 'size': 8},
        'Bool8': {'code': 17, 'fmt': 'B', 'size': 1},
        'Bool': {'code': 10, 'fmt': 'B', 'size': 1},
        'Bool2': {'code': 27, 'fmt': '>H', 'size': 2},
        'Bool4': {'code': 28, 'fmt': '>L', 'size': 4},
        'Sec': {'code': 12, 'fmt': '>l', 'size': 4},
        'USec': {'code': 13, 'fmt': '6c', 'size': 6},
        'NSec': {'code': 14, 'fmt': '>2l', 'size': 8},
        'ASCII': {'code': 11, 'fmt': 's', 'size': None},
        'ASCIIZ': {'code': 16, 'fmt': 's', 'size': None},
        'Short': {'code': 19, 'fmt': '<h', 'size': 2},
        'Long': {'code': 20, 'fmt': '<l', 'size': 4},
        'UShort': {'code': 21, 'fmt': '<H', 'size': 2},
        'ULong': {'code': 22, 'fmt': '<L', 'size': 4},
        'IEEE4L': {'code': 24, 'fmt': '<f', 'size': 4},
        'IEEE8L': {'code': 25, 'fmt': '<d', 'size': 8},
        'SecNano': {'code': 23, 'fmt': '<2l', 'size': 8},
    }

    # link state
    RING = 0x9
    READY = 0xA
    FINISHED = 0xB

    def __init__(self, link, dest_addr=None, dest=0x001,
                 src_addr=None, src=0x802, security_code=0x0000):
        self.link = link
        self.src = src
        if (src_addr != None):
            self.src_addr = src_addr
        else:
            self.src_addr = src
        self.dest = dest
        if (dest_addr != None):
            self.dest_addr = dest_addr
        else:
            self.dest_addr = dest
        self.security_code = security_code
        self.transaction = Transaction()
        LOGGER.info('Get the node attention')
        self.link.write(b'\xBD\xBD\xBD\xBD\xBD\xBD')
              

    def write(self, packet):
        '''Send packet over PakBus.'''
        LOGGER.info('Packet data: %s' % bytes_to_hex(packet))
        LOGGER.info('Calculate signature for packet')
        sign = self.compute_signature(packet)
        LOGGER.info('Calculate signature nullifier to create packet')
        nullifier = self.compute_signature_nullifier(sign)
        frame = self.quote(b''.join((packet, nullifier)))
        packet = b''.join((b'\xBD', frame, b'\xBD'))
        LOGGER.info('Write: %s' % bytes_to_hex(packet))
        self.link.write(packet)

    def read(self):
        '''Receive packet over PakBus.'''
        all_bytes = []
        byte = None
        begin = time.time()
        while byte != b'\xBD':
            if (byte != None):
                LOGGER.info('Read byte: %s' % bytes_to_hex(byte))
            if time.time() - begin > self.link.timeout:
                return None
            # Read until first \xBD frame character
            byte = self._read_one_byte()
        while byte == b'\xBD':
            # Read unitl first character other than \xBD
            byte = self._read_one_byte()
        while byte != b'\xBD':
            # Read until next occurence of \xBD character
            all_bytes.append(byte)
            byte = self._read_one_byte()

        # Unquote quoted characters
        packet = b''.join(all_bytes)
        LOGGER.info('Read packet: %s' % bytes_to_hex(packet))
        packet = self.unquote(packet)

        # Calculate signature (should be zero)
        if self.compute_signature(packet):
            LOGGER.error('Check signature : Error')
            return None
        else:
            LOGGER.info('Check signature : OK')
            # Strip last 2 signature bytes and return packet
            return packet[:-2]

    def _read_one_byte(self):
        '''Read only one byte.'''
        data = self.link.read(1)
        if is_text(data):
            return bytes(data.encode('utf-8'))
        else:
            return data

    def wait_packet(self, transac_id=None):
        '''Wait for an incoming packet.

        :param transac_id: Expected transaction number.
        '''
        LOGGER.info('Wait packet with transaction %s' % transac_id)
        data = self.read()
        if data is None or data == b'':
            return {}, {}

        hdr, msg = self.decode_packet(data)
        if hdr == {} or msg == {}:
            return hdr, msg

        # ignore packets that are not for us

        LOGGER.info('src, SrcNodeId = <%x, %x>' %
                    (self.src, hdr['SrcNodeId']))
        LOGGER.info('dest, DstNodeId = <%x, %x>' %
                    (self.dest, hdr['DstNodeId']))

        if (hdr['DstNodeId'] != self.src):
            return {}, {}

        # Handle 'please wait' packets
        if msg['TranNbr'] == transac_id and msg['MsgType'] == 0xa1:
            timewait = msg['WaitSec']
            LOGGER.info('Please Wait Message packet <%s sec>' % timewait)
            time.sleep(timewait)
            return self.wait_packet(transac_id)

        # Handle failure message packets and raise exception
        if msg['MsgType'] == 0x81:
            raise DeliveryFailureException()

        # This should be the packet we are waiting for
        if msg['TranNbr'] == transac_id:
            return hdr, msg

    def pack_header(self, hi_proto, exp_more=0x2, link_state=None,
                    hops=0x0):
        '''Generate PakBus header.

        :param hi_proto: Higher level protocol code (4 bits). 0x0: PakCtrl,
                         0x1: BMP5
        :param exp_more: Whether client should expect another packet (2 bits)
        :param link_state: Link state (4 bits)
        :param hops: Number of hops to destination (4 bits)
        '''
        LOGGER.info('Create header')
        priority = 0x1
        link_state = link_state or self.READY
        # bitwise encoding of header fields
        hdr = struct.pack(str('>4H'),
                          (link_state & 0xF) << 12 | (self.dest_addr & 0xFFF),
                          (exp_more & 0x3) << 14 | (priority & 0x3) << 12
                                                 | (self.src_addr & 0xFFF),
                          (hi_proto & 0xF) << 12 | (self.dest & 0xFFF),
                          (hops & 0xF) << 12 | (self.src & 0xFFF))
        return hdr

    def compute_signature(self, buff, seed=0xAAAA):
        '''Compute signature for PakBus packets.'''
        sig = seed
        for x in buff:
            x = ord(x)
            j = sig
            sig = (sig << 1) & 0x1FF
            if sig >= 0x100:
                sig += 1
            sig = ((((sig + (j >> 8) + x) & 0xFF) | (j << 8))) & 0xFFFF
        return sig

    def compute_signature_nullifier(self, sig):
        '''Compute signature nullifier needed to create valid PakBus
        packets.'''
        nulb = nullif = b''
        for i in (1, 2):
            sig = self.compute_signature(nulb, sig)
            sig2 = (sig << 1) & 0x1FF
            if sig2 >= 0x100:
                sig2 += 1
            nulb = chr((0x100 - (sig2 + (sig >> 8))) & 0xFF)
            nullif += nulb
        return nullif

    def quote(self, packet):
        '''Quote the PakBus packet.'''
        LOGGER.info('Quote packet')
        packet = packet.replace(b'\xBC', b'\xBC\xDC')
        packet = packet.replace(b'\xBD', b'\xBC\xDD')
        return packet

    def unquote(self, packet):
        '''Unquote the PakBus packet.'''
        LOGGER.info('Unquote packet')
        packet = packet.replace(b'\xBC\xDD', b'\xBD')
        packet = packet.replace(b'\xBC\xDC', b'\xBC')
        return packet

    def encode_bin(self, types, values):
        '''Encode binary data according to data type.'''
        buff = []  # buffer for binary data
        for i, type_ in enumerate(types):
            fmt = self.DATATYPE[type_]['fmt']  # get default format for type_
            value = values[i]

            if type_ == 'ASCIIZ':
                # special handling: nul-terminated string
                value += '\0'
                # Add nul to end of string
                fmt_ = str('%d%s' % (len(value), fmt))
                if is_py3:
                    enc = struct.pack(fmt_, bytes(value, encoding='utf8'))
                else:
                    enc = struct.pack(fmt_, str(value))
            elif type_ == 'ASCII':
                # special handling: fixed-length string
                fmt_ = str('%d%s' % (len(value), fmt))
                enc = struct.pack(fmt_, str(value))
            elif type_ == 'NSec':
                # special handling: NSec time
                enc = struct.pack(str(fmt), value[0], value[1])
            else:
                # default encoding scheme
                enc = struct.pack(str(fmt), value)

            buff.append(enc)
        return b''.join(buff)

    def decode_bin(self, types, buff, length=1):
        '''Decode binary data according to data type.'''
        offset = 0  # offset into buffer
        values = []  # list of values to return
        for type_ in types:
            # get default format and size for Type
            fmt = self.DATATYPE[type_]['fmt']
            size = self.DATATYPE[type_]['size']

            if type_ == 'ASCIIZ':
                # special handling: nul-terminated string
                nul = buff.index(b'\0', offset)
                # find first '\0' after offset
                if nul == -1:
                    value = buff[offset:]
                else:
                    value = buff[offset:nul]
                # return string without trailing '\0'
                size = len(value) + 1
            elif type_ == 'ASCII':
                # special handling: fixed-length string
                size = length
                value = buff[offset:offset + size]
                # return fixed-length string
            elif type_ == 'FP2':
                # special handling: FP2 floating point number
                fp2 = struct.unpack(str(fmt), buff[offset:offset + size])
                mant = fp2[0] & 0x1FFF    # mantissa is in bits 1-13
                exp = fp2[0] >> 13 & 0x3  # exponent is in bits 14-15
                sign = fp2[0] >> 15       # sign is in bit 16
                value = ((-1) ** sign * float(mant) / 10 ** exp, )
            else:
                # default decoding scheme
                if buff[offset:offset + size]:
                    value = struct.unpack(str(fmt), buff[offset:offset + size])
                else:
                    value = ''

            # un-tuple single values
            if len(value) == 1:
                value = value[0]

            values.append(value)
            offset += size
        # Return decoded values and current offset into buffer (size)
        return values, offset

    def decode_packet(self, data):
        '''Decode packet from raw data.'''
        LOGGER.info('Decode packet')
        # pkt: buffer containing unquoted packet, signature nullifier stripped
        # Initialize output variables
        hdr = {'LinkState': None, 'DstPhyAddr': None, 'ExpMoreCode': None,
               'Priority': None, 'SrcPhyAddr': None, 'HiProtoCode': None,
               'DstNodeId': None, 'HopCnt': None, 'SrcNodeId': None}
        msg = {'MsgType': None, 'TranNbr': None, 'raw': None}

        # decode PakBus header
        rawhdr = struct.unpack(str('>4H'), data[0:8])  # raw header bits
        hdr['LinkState'] = rawhdr[0] >> 12
        hdr['DstPhyAddr'] = rawhdr[0] & 0x0FFF
        hdr['ExpMoreCode'] = (rawhdr[1] & 0xC000) >> 14
        hdr['Priority'] = (rawhdr[1] & 0x3000) >> 12
        hdr['SrcPhyAddr'] = rawhdr[1] & 0x0FFF
        hdr['HiProtoCode'] = rawhdr[2] >> 12
        hdr['DstNodeId'] = rawhdr[2] & 0x0FFF
        hdr['HopCnt'] = rawhdr[3] >> 12
        hdr['SrcNodeId'] = rawhdr[3] & 0x0FFF

        # decode default message fields:
        # raw message, message type and transaction number
        msg['raw'] = data[8:]
        values, size = self.decode_bin(('Byte', 'Byte'), msg['raw'][:2])
        msg['MsgType'], msg['TranNbr'] = values
        LOGGER.info('HiProtoCode, MsgType = <%x, %x>' %
                    (hdr['HiProtoCode'], msg['MsgType']))

        # PakBus Control Packets
        if hdr['HiProtoCode'] == 0 and msg['MsgType'] in (0x09, 0x89, 0xe):
            msg = self.unpack_hello_response(msg)
        elif hdr['HiProtoCode'] == 0 and msg['MsgType'] == 0x81:
            msg = self.unpack_failure_response(msg)
        elif hdr['HiProtoCode'] == 0 and msg['MsgType'] == 0x8f:
            msg = self.unpack_getsettings_response(msg)
        # BMP5 Application Packets
        elif hdr['HiProtoCode'] == 1 and msg['MsgType'] == 0x89:
            msg = self.unpack_collectdata_response(msg)
        elif hdr['HiProtoCode'] == 1 and msg['MsgType'] == 0x97:
            msg = self.unpack_clock_response(msg)
        elif hdr['HiProtoCode'] == 1 and msg['MsgType'] == 0x98:
            msg = self.unpack_getprogstat_response(msg)
        elif hdr['HiProtoCode'] == 1 and msg['MsgType'] == 0x9c:
            msg = self.unpack_filedownload_response(msg)
        elif hdr['HiProtoCode'] == 1 and msg['MsgType'] == 0x9d:
            msg = self.unpack_fileupload_response(msg)
        elif hdr['HiProtoCode'] == 1 and msg['MsgType'] == 0xa1:
            msg = self.unpack_pleasewait_response(msg)
        else:
            LOGGER.error('No implementation for <(%r, %r)> packet type'
                         % (hdr['HiProtoCode'], msg['MsgType']))
        return hdr, msg

    def get_hello_cmd(self):
        '''Create Hello Command packet.'''
        transac_id = self.transaction.next_id()
        hdr = self.pack_header(0x0, 0x1, self.RING)
        msg = self.encode_bin(['Byte', 'Byte', 'Byte', 'Byte', 'UInt2'],
                              [0x09, transac_id, 0x00, 0x02, 1800])
        return b''.join((hdr, msg)), transac_id

    def get_hello_response(self, transac_id):
        '''Create Hello Response packet.'''
        hdr = self.pack_header(0x0)
        msg = self.encode_bin(['Byte', 'Byte', 'Byte', 'Byte', 'UInt2'],
                              [0x89, transac_id, 0x00, 0x02, 1800])
        return b''.join([hdr, msg])

    def unpack_hello_response(self, msg):
        '''Create Hello Response packet.'''
        raw = msg['raw'][2:]
        values, size = self.decode_bin(['Byte', 'Byte', 'UInt2'], raw)
        msg['IsRouter'], msg['HopMetric'], msg['VerifyIntv'] = values
        return msg

    def unpack_failure_response(self, msg):
        '''Unpack Failure Response packet.'''
        (msg['ErrCode'],), size = self.decode_bin(['Byte'], msg['raw'][2:])
        return msg

    def get_getsettings_cmd(self):
        '''Create Getsettings Command packet.'''
        transac_id = self.transaction.next_id()
        hdr = self.pack_header(0x0)
        msg = self.encode_bin(['Byte', 'Byte'], [0x0f, transac_id])
        return b''.join([hdr, msg]), transac_id

    def unpack_getsettings_response(self, msg):
        '''Unpack Getsettings Response packet.'''
        (msg['Outcome'],), size = self.decode_bin(['Byte'], msg['raw'][2:])
        offset = size + 2

        # Generate dictionary of all settings
        msg['Settings'] = []
        if msg['Outcome'] == 0x01:
            values, size = self.decode_bin(['UInt2', 'Byte', 'Byte', 'Byte'],
                                           msg['raw'][offset:])
            msg['DeviceType'] = values[0]
            msg['MajorVersion'] = values[1]
            msg['MinorVersion'] = values[2]
            msg['MoreSettings'] = values[3]
            offset += size

            while offset < len(msg['raw']):
                # Get setting ID
                [SettingId], size = self.decode_bin(['UInt2'],
                                                    msg['raw'][offset:])
                offset += size
                if not msg['raw'][offset:]:
                    break
                # Get flags and length
                [bit16], size = self.decode_bin(['UInt2'], msg['raw'][offset:])
                LargeValue = (bit16 & 0x8000) >> 15
                ReadOnly = (bit16 & 0x4000) >> 14
                SettingLen = bit16 & 0x3FFF
                offset += size

                # Get value
                SettingValue = msg['raw'][offset:offset + SettingLen]
                offset += SettingLen
                item = {'SettingId': SettingId,
                        'SettingValue': SettingValue,
                        'LargeValue': LargeValue,
                        'ReadOnly': ReadOnly}
                msg['Settings'].append(item)
        return msg

    def get_collectdata_cmd(self, tablenbr, tabledefsig, mode=0x04, p1=0,
                            p2=0):
        '''Create Collect Data Command packet

        :param tablenbr: Table number that contain data.
        :param tabledefsig: Table definition signature.
        :param mode: Collection mode code (p1 and p2 will be used depending on
                    value).
        :param p1: 1st parameter used to specify what to collect (optional)
        :param p2: 2nd parameter used to specify what to collect (optional)
        '''
        transac_id = self.transaction.next_id()
        # BMP5 Application Packet
        hdr = self.pack_header(0x1)
        msg = self.encode_bin(['Byte', 'Byte', 'UInt2', 'Byte'],
                              [0x09, transac_id, self.security_code,
                               mode])
        # encode table number and signature
        msg += self.encode_bin(['UInt2', 'UInt2'], [tablenbr, tabledefsig])
        # add p1 and p2 according to collect mode
        if (mode == 0x04) | (mode == 0x05):
            # only P1 used (type UInt4)
            msg += self.encode_bin(['UInt4'], [p1])
        elif (mode == 0x06) | (mode == 0x08):
            # P1 and P2 used (type UInt4)
            msg += self.encode_bin(['UInt4', 'UInt4'], [p1, p2])
        elif mode == 0x07:
            # P1 and P2 used (type NSec)
            msg += self.encode_bin(['NSec', 'NSec'], [p1, p2])
        # add field list = all fields
        msg += self.encode_bin(['UInt2'], [0])
        return b''.join((hdr, msg)), transac_id

    def unpack_collectdata_response(self, msg):
        '''Unpack Collect Data Response body.'''
        (msg['RespCode'],), size = self.decode_bin(['Byte'], msg['raw'][2:])
        # return raw record data for later parsing
        msg['RecData'] = msg['raw'][size + 2:]
        return msg

    def get_clock_cmd(self, adjustment=(0, 0)):
        '''Create Clock Command packet.

        :param adjustment: Clock adjustment (seconds, nanoseconds).
        '''
        transac_id = self.transaction.next_id()
        # BMP5 Application Packet
        hdr = self.pack_header(0x1)
        msg = self.encode_bin(['Byte', 'Byte', 'UInt2', 'NSec'],
                              [0x17, transac_id, self.security_code,
                               adjustment])
        return b''.join((hdr, msg)), transac_id

    def unpack_clock_response(self, msg):
        '''Unpack Clock Response packet.'''
        values, size = self.decode_bin(['Byte', 'NSec'], msg['raw'][2:])
        msg['RespCode'], msg['Time'] = values
        return msg

    def get_getprogstat_cmd(self):
        '''Create Get Programming Statistics Transaction packet.'''
        transac_id = self.transaction.next_id()
        # BMP5 Application Packet
        hdr = self.pack_header(0x1)
        msg = self.encode_bin(['Byte', 'Byte', 'UInt2'],
                              [0x18, transac_id, self.security_code])
        return b''.join((hdr, msg)), transac_id

    def unpack_getprogstat_response(self, msg):
        '''Unpack Get Programming Statistics Response packet.'''
        # Get response code
        (msg['RespCode'], ), size = self.decode_bin(['Byte'], msg['raw'][2:])

        # Get report data if RespCode == 0
        if msg['RespCode'] == 0:
            types = ['ASCIIZ', 'UInt2', 'ASCIIZ', 'ASCIIZ', 'Byte', 'ASCIIZ',
                     'UInt2', 'NSec', 'ASCIIZ']
            values, size = self.decode_bin(types, msg['raw'][3:])
            item = {'OSVer': values[0], 'OSSig': values[1],
                    'SerialNbr': values[2], 'PowUpProg': values[3],
                    'CompState': values[4], 'ProgName': values[5],
                    'ProgSig': values[6], 'CompTime': values[7],
                    'CompResult': values[8]}
            msg['Stats'] = item
        return msg

    def get_filedownload_cmd(self, filename, data, offset=0x00000000,
                             closeflag=0x00, transac_id=None):
        '''Create Filedownload Command packet.

        :param filename: File name as string
        :param offset: Byte offset into the file or fragment
        :param closeflag: Flag if file should be closed after this transaction
        :param transac_id: Transaction number for continuing partial reads
        '''
        raise NotImplementedError('Filedownload transaction is not implemented'
                                  ' yet')

    def unpack_filedownload_response(self, msg):
        '''Unpack Filedownload Response packet.'''
        raise NotImplementedError('Filedownload transaction is not implemented'
                                  ' yet')

    def get_fileupload_cmd(self, filename, offset=0x00000000, swath=0x0200,
                           closeflag=0x01, transac_id=None):
        '''Create Fileupload Command packet.

        :param filename: File name as string
        :param offset: Byte offset into the file or fragment
        :param swath: Number of bytes to read
        :param closeflag: Flag if file should be closed after this transaction
        :param transac_id: Transaction number for continuing partial reads
                           (required by OS>=17!)
        '''
        if transac_id is None:
            transac_id = self.transaction.next_id()
        # BMP5 Application Packet
        hdr = self.pack_header(0x1)
        types = ['Byte', 'Byte', 'UInt2', 'ASCIIZ', 'Byte', 'UInt4', 'UInt2']
        values = [0x1d, transac_id, self.security_code, filename, closeflag,
                  offset, swath]
        msg = self.encode_bin(types, values)
        return b''.join((hdr, msg)), transac_id

    def unpack_fileupload_response(self, msg):
        '''Unpack Fileupload Response packet.'''
        values, size = self.decode_bin(['Byte', 'UInt4'], msg['raw'][2:7])
        msg['RespCode'], msg['FileOffset'] = values
        msg['FileData'] = msg['raw'][7:]
        return msg

    def unpack_pleasewait_response(self, msg):
        '''Unpack PeaseWait Response packet.'''
        values, size = self.decode_bin(['Byte', 'UInt2'], msg['raw'][2:])
        msg['CmdMsgType'], msg['WaitSec'] = values
        return msg

    def get_bye_cmd(self):
        '''Create Bye Command packet.'''
        transac_id = self.transaction.next_id()
        # PakBus Control Packet
        hdr = self.pack_header(0x0, 0x0, self.FINISHED)
        msg = self.encode_bin(['Byte', 'Byte'], [0x0d, 0x0])
        return b''.join((hdr, msg)), transac_id

    def parse_filedir(self, data):
        '''Parse file directory format.'''
        offset = 0  # offset into raw buffer
        fd = {'files': []}  # initialize file directory structure
        [fd['DirVersion']], size = self.decode_bin(['Byte'], data[offset:])
        offset += size
        # Extract file entries
        while len(data) > offset:
            file_ = {}  # file description
            [filename], size = self.decode_bin(['ASCIIZ'], data[offset:])
            offset += size

            # end loop when file attribute list terminator reached
            if filename == '':
                break

            file_['FileName'] = filename
            values, size = self.decode_bin(['UInt4', 'ASCIIZ'], data[offset:])
            file_['FileSize'], file_['LastUpdate'] = values
            offset += size

            # Read file attribute list
            file_['Attribute'] = []
            # initialize file attribute list (up to 12)
            for i in range(12):
                [attribute], size = self.decode_bin(['Byte'], data[offset:])
                offset += size
                if attribute:
                    # append file attribute to list
                    file_['Attribute'].append(attribute)
                else:
                    break  # End of attribute list reached
            fd['files'].append(file_)  # add file entry to list
        return fd

    def parse_tabledef(self, raw):
        '''Parse table definition.'''
        tabledef = []  # List of table definitions
        offset = 0  # offset into raw buffer
        fslversion, size = self.decode_bin(['Byte'], raw[offset:])
        offset += size

        # Parse list of table definitions
        while offset < len(raw):

            tblhdr = {}  # table header
            tblfld = []  # table field definitions
            start = offset  # start of table definition

            # Extract table header data
            types = ['ASCIIZ', 'UInt4', 'Byte', 'NSec', 'NSec']
            values, size = self.decode_bin(types, raw[offset:])
            tblhdr['TableName'] = values[0]
            tblhdr['TableSize'] = values[1]
            tblhdr['TimeType'] = values[2]
            tblhdr['TblTimeInto'] = values[3]
            tblhdr['TblInterval'] = values[4]

            offset += size

            # Extract field definitions
            (fieldtype,), size = self.decode_bin(['Byte'], raw[offset:])
            offset += size
            while fieldtype != 0:
                fld = {}

                # Extract bits from fieldtype
                fld['ReadOnly'] = fieldtype >> 7  # only Bit 7

                # Convert fieldtype to ASCII FieldType (e.g. 'FP4') if possible
                # else return numerical value
                fld['FieldType'] = fieldtype & 0x7F  # only Bits 0..6
                for Type in self.DATATYPE.keys():
                    if fld['FieldType'] == self.DATATYPE[Type]['code']:
                        fld['FieldType'] = Type
                        break

                # Extract field name
                values, size = self.decode_bin(['ASCIIZ'], raw[offset:])
                fld['FieldName'] = values[0]
                offset += size

                # Extract AliasName list
                fld['AliasName'] = []
                aliasname = b'00'
                # Alias names list terminator reached
                while aliasname != b'':
                    values, size = self.decode_bin(['ASCIIZ'], raw[offset:])
                    aliasname = values[0]
                    offset += size
                    if aliasname != b'':
                        fld['AliasName'].append(aliasname)

                # Extract other mandatory field definition items
                types = ['ASCIIZ', 'ASCIIZ', 'ASCIIZ', 'UInt4', 'UInt4']
                values, size = self.decode_bin(types, raw[offset:])
                fld['Processing'] = values[0]
                fld['Units'] = values[1]
                fld['Description'] = values[2]
                fld['BegIdx'] = values[3]
                fld['Dimension'] = values[4]
                offset += size

                # Extract sub dimension (if any)
                fld['SubDim'] = []
                subdim = 1
                # sub-dimension list terminator reached
                while subdim != 0:
                    (subdim,), size = self.decode_bin(['UInt4'], raw[offset:])
                    offset += size
                    if subdim != 0:
                        fld['SubDim'].append(subdim)

                # append current field definition to list
                tblfld.append(fld)

                (fieldtype,), size = self.decode_bin(['Byte'], raw[offset:])
                offset += size
            # calculate table signature
            tblsig = self.compute_signature(raw[start:offset])

            # Append header, field list and signature to table definition list
            item = {'Header': tblhdr, 'Fields': tblfld, 'Signature': tblsig}
            tabledef.append(item)
        return tabledef

    def parse_collectdata(self, raw, tabledef, fieldnbr=[]):
        '''Parse data returned by Collectdata Response.'''
        offset = 0
        recdata = []  # output structure

        while offset < len(raw) - 1:
            frag = {}  # record fragment

            values, size = self.decode_bin(['UInt2', 'UInt4'], raw[offset:])
            frag['TableNbr'], frag['BegRecNbr'] = values
            offset += size

            # Provide table name
            t_frag = tabledef[frag['TableNbr'] - 1]
            tablename = t_frag['Header']['TableName']
            frag['TableName'] = tablename

            # Decode number of records (16 bits) or ByteOffset (32 Bits)
            (isoffset,), size = self.decode_bin(['Byte'], raw[offset:])
            frag['IsOffset'] = isoffset >> 7

            # Handle fragmented records
            if frag['IsOffset']:
                (byteoffset,), size = self.decode_bin(['UInt4'], raw[offset:])
                offset += size
                frag['ByteOffset'] = byteoffset & 0x7FFFFFFF
                frag['NbrOfRecs'] = None
                # Copy remaining raw data into RecFrag
                frag['RecFrag'] = raw[offset:-1]
                offset += len(frag['RecFrag'])

            # Handle complete records (standard case)
            else:
                (nbrofrecs,), size = self.decode_bin(['UInt2'], raw[offset:])
                offset += size
                frag['NbrOfRecs'] = nbrofrecs & 0x7FFF
                frag['ByteOffset'] = None

                # Get time of first record and time interval information
                interval = t_frag['Header']['TblInterval']
                if interval == (0, 0):  # event-driven table
                    timeofrec = None
                else:
                    # interval data, read time of first record
                    [timeofrec], size = self.decode_bin(['NSec'], raw[offset:])
                    offset += size

                # Loop over all records
                frag['RecFrag'] = []
                for n in range(frag['NbrOfRecs']):
                    record = {}

                    # Calculate current record number
                    record['RecNbr'] = frag['BegRecNbr'] + n

                    # Get TimeOfRec for interval data or event-driven tables
                    if timeofrec:  # interval data
                        next_timeofrec = (timeofrec[0] + n * interval[0],
                                          timeofrec[1] + n * interval[1])
                        record['TimeOfRec'] = nsec_to_time(next_timeofrec)
                    else:
                        # event-driven, time data precedes each record
                        values, size = self.decode_bin(['NSec'], raw[offset:])
                        record['TimeOfRec'] = values[0]
                        record['TimeOfRec'] = nsec_to_time(record['TimeOfRec'])
                        offset += size

                    # Loop over all field indices
                    record['Fields'] = {}
                    if fieldnbr:
                        # explicit field numbers provided
                        fields = fieldnbr
                    else:
                        # default: generate list of all fields in table
                        fields = t_frag['Fields']
                        fields = range(1, len(fields) + 1)

                    for field in fields:
                        fieldname = t_frag['Fields'][field - 1]['FieldName']
                        fieldtype = t_frag['Fields'][field - 1]['FieldType']
                        dimension = t_frag['Fields'][field - 1]['Dimension']
                        if fieldtype == 'ASCII':
                            values, size = self.decode_bin([fieldtype],
                                                           raw[offset:],
                                                           dimension)
                            record['Fields'][fieldname] = values[0]
                        else:
                            values, size = \
                                self.decode_bin(dimension * [fieldtype],
                                                raw[offset:])
                            record['Fields'][fieldname] = values[0]
                        offset += size
                    frag['RecFrag'].append(record)

            recdata.append(frag)

        # Get flag if more records exist
        (more_rec,), size = self.decode_bin(['Bool'], raw[offset:])
        return recdata, more_rec

    def __del__(self):
        self.link.close()

    def __str__(self):
        name = self.__class__.__name__
        return '<%s %s>' % (name, self.link)

    def __repr__(self):
        return '%s' % self.__str__()
Error:
Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license()" for more information. >>> ============== RESTART: C:\Users\Makada\Desktop\cr1000 - kopie1.py ============= Device is connected: True 2019-11-17 20:42:31 Traceback (most recent call last): File "C:\Users\Makada\Desktop\cr1000 - kopie1.py", line 7, in <module> print(device.list_tables()) File "C:\Program Files\Python\lib\site-packages\pycampbellcr1000\device.py", line 182, in list_tables return [item['Header']['TableName'] for item in self.table_def] File "C:\Program Files\Python\lib\site-packages\pycampbellcr1000\utils.py", line 65, in __get__ value = self.func(obj) File "C:\Program Files\Python\lib\site-packages\pycampbellcr1000\device.py", line 177, in table_def tabledef = self.pakbus.parse_tabledef(data) File "C:\Program Files\Python\lib\site-packages\pycampbellcr1000\pakbus.py", line 700, in parse_tabledef values, size = self.decode_bin(types, raw[offset:]) File "C:\Program Files\Python\lib\site-packages\pycampbellcr1000\pakbus.py", line 318, in decode_bin value = struct.unpack(str(fmt), buff[offset:offset + size]) struct.error: unpack requires a buffer of 4 bytes >>>
Reply
#5
I made an assumption (bad thing to do) that you had defined buffer.
So, what did you want to display here?
Obviously if buffer was never defined, it can't be displayed.
Reply
#6
Hi,

Sorry i really dont know what to change in the above py files.
they are within the pycampbellcr1000 py Package and should be running ok, but it doesnt.
Is it possible you can guide me through the code to get data from the datalogger?

With kind regards,

Mark
Reply
#7
Strangely enough, that part of the code works good for me in Python 3.7.3 (Linux):

Python 3.7.3 (default, May 11 2019, 00:38:04) 
[GCC 9.1.1 20190503 (Red Hat 9.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> stdout = sys.stdout.buffer
>>> type(stdout)
<class '_io.BufferedWriter'>
Reply
#8
Hi,

I think because im on windows 10 why its not working...

With kind regards.

Mark
Reply
#9
Please have a look here: https://pypi.org/project/PyCampbellCR1000/
Version 0.4 of this package was Released on 2017-11-28
and supports Python 3.4

It´s not advised to install latest version of programming language and install old modules.
Reply
#10
Hi,

Thanks for your reply.
Is it really that sensitive? Im surprised it depends on the python version if a module is working or not.
But im New to python and maybe thats normal and version dependent if working or not.
So for now its maybe adviceable to rolls back to python 3.4 from 3.8.
Do i have to uninstall 3.8 first or will it roll back to 3.4 when m running the 3.4 installer?
Ofcourse i want to have the latest version of Python with latest features but if that cause to not run older modules its not going to work.

With kind regards,

Mark

Unfortunately with Python 3.4 i have the same error.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020