www.gusucode.com > Zaber Device Control Toolbox > +Zaber/BinaryMessage.m

    classdef BinaryMessage < handle
%   BINARYMESSAGE Helper class for Zaber Binary protocol messages.
%   This class helps serialize and deserialize 6-byte binary messages 
%   used by the Zaber Binary protocol into their constituent parts.
%
%   See also BinaryProtocol, Device, BinaryDevice

%   Author: Zaber Technologies Software Team <contact@zaber.com>

    
%% Public instance properties
    properties
        % DEVICENO The address of a device being addressed.
        % For requests this will be the device the command is addressed
        % to, in the range of 1-99, or 0 to address all devices.
        % In replies this will be the address of the device responding.
        DeviceNo
        
        % COMMAND The command being issued or responded to.
        % Legal values are 0-255. See the Zaber Binary Protocol 
        % Manual for the meanings of the values:
        % https://www.zaber.com/wiki/Manuals/Binary_Protocol_Manual or use
        % the Zaber.BinaryCommandType enumeration.
        %
        % See also Data, Zaber.BinaryCommandType, Zaber.BinaryReplyType
        Command
        
        % MESSAGEID Optional message ID for correlating messages.
        % If message ID mode is enabled on a device, this value will
        % be echoed back by the device in its replies. Otherwise
        % the value should be zero. Note that use of message IDs
        % reduces the number of data bits available from 32 to 24.
        %
        % See also Data
        MessageId
        
        % DATA Data payload for requests and responses.
        % A signed integer. 32 bits, or 24 bits if message ID mode is
        % enabled, in which case this property is automatically 
        % sign-extended to 32 bits after deserialization.
        %
        % See also Command, MessageId
        Data
        
        % ISERROR True if this message represents a device error state.
        IsError
        
        % MESSAGETYPE Identifies whether this message is a request (a
        % command for a device) or a response (a reply from the device).
        MessageType
    end
    
 
 %% Public instance methods
    methods
        function obj = BinaryMessage(aDeviceNumber, aCommand, aData, aMessageId)
        % BINARYMESSAGE Construct a BinaryMessage object from values.
        % message = Zaber.BINARYMESSAGE(address, command, data)
        % message = Zaber.BINARYMESSAGE(address, command, data, messageid)
        %
        % address   - The daisy chain address of the device to talk to.
        % command   - The command or reply code for the message.
        % data      - The data payload. Use 0 when not applicable.
        % messageid - Optional message ID.
        %
        % This function constructs an instance of the BinaryMessage
        % class with its properties initialized. See the property
        % descriptions for information about the corresponding arguments. 
        %
        % NOTE
        % The message ID argument is optional and should only be used if
        % message ID mode is enabled on the device the command is being
        % sent to.

            if ((aDeviceNumber < 0) || (aDeviceNumber > 99))
                error('Zaber:BinaryMessage:badDeviceNo', ...
                      'Zaber device IDs must range from 0 to 99.');
            end
            
            obj.DeviceNo = uint8(aDeviceNumber);
            obj.MessageType = Zaber.MessageType.Request;
            
            if ((aCommand < 0) || (aCommand > 255))
                error('Zaber:Binarymessage:badCommandNo', ...
                      'Zaber binary command numbers must range from 0 to 255.');
            end
            
            obj.Command = uint8(aCommand);
            obj.IsError = (obj.Command == Zaber.BinaryReplyType.Error);
            
            val = int32(aData);
            
            if (nargin < 4)
                obj.MessageId = uint8(0);
            else
                if ((aMessageId < 0) || (aMessageId > 255))
                    error('Zaber:BinaryMessage:badMessageId', ...
                          'Message IDs must be in the range of 0 to 255.');
                end
                
                obj.MessageId = uint8(aMessageId);
            end
            
            obj.Data = val;
        end
        
        
        function bytes = serialize(obj)
        % SERIALIZE Convert to an array of bytes suitable for transmission.
        % bytes = message.SERIALIZE()
        %
        % Returns an array of six bytes ready to be transmitted to a
        % Zaber device using the Binary protocol.
        %
        % If the MessageId property is nonzero, this will truncate the
        % Data value to 24 bits and throw an error if doing so would
        % cause data loss.
        %
        % See also deserialize
        
            bytes = [ obj.DeviceNo obj.Command ];
            
            val = int64(obj.Data);
            if (val < 0)
                val = val + 256^4;
            end
            
            val = uint32(val);
            
            maxData = intmax('uint32');
            if (obj.MessageId > 0)
                maxData = 256 ^ 3 - 1;
            end
            
            if ((val < 0) || (val > maxData))
                error('Zaber:BinaryMessage:serialize:outOfRange', ...
                      'Data value ' + num2str(obj.Data) + ' cannot be represented.');
            end
            
            if (obj.MessageId > 0)
                for (i = 1:3)
                    byte = bitand(val, 255);
                    bytes = [bytes byte];
                    val = bitshift(val, -8);
                end
                bytes = [ bytes obj.MessageId ];
            else
                for (i = 1:4)
                    byte = bitand(val, 255);
                    bytes = [bytes byte];
                    val = bitshift(val, -8);
                end
            end            
        end
    end
    
    
 %% Public static methods
    methods (Static)
        function obj = deserialize(aBytes, aMessageIdsEnabled)
        % DESERIALIZE Convert an array of bytes to a BinaryMessage instance.
        % message = Zaber.BinaryMessage.DESERIALIZE(bytes)
        % message = Zaber.BinaryMessage.DESERIALIZE(bytes, hasid)
        %
        % bytes   - An array of six bytes received from a device.
        % hasid   - Optional. Pass in true if the device is using
        %           message IDs. This will cause the ID to be separated
        %           from the data. 
        % message - The deserialized BinaryMessage instance.
        %
        % Given an array of 6 values in the range of 0 to 255, this
        % will construct a corresponding BinaryMessage class with the
        % properties filled in accordingly. Use this to convert reply
        % data from a device into a more convenient form.
        %
        % NOTE: It is not possible to tell from the data directly if
        % message ID mode is enabled on the device, so the caller must
        % pass in true for the optional ID enabled argument if that mode
        % is in use. In that case, the MessageId field will be filled in
        % and the data payload will be sign-extended from 24 to 32 bits.
        %
        % See also serialize
        
            if (length(aBytes) ~= 6)
                error('Zaber:BinaryMessage:deserialize:badPacketLength', ...
                      'Binary packets must have 6 bytes');
            end
            
            for (i = 1:6)
                if ((aBytes(i) < 0) || (aBytes(i) > 255))
                    error('Zaber:Binarymessage:deserialize:badDataType', ...
                          'Binary packet elements must be representable as bytes.');
                end
            end
                        
            deviceNumber = aBytes(1);
            command = aBytes(2);
            
            data = int32(0);
            if ((nargin > 1) && aMessageIdsEnabled())
                messageId = aBytes(6);
                for (i = 5:-1:3)
                    data = bitshift(data, 8) + aBytes(i);
                end
                
                data = bitshift(bitshift(data, 8), -8); % Sign extend
                
                obj = Zaber.BinaryMessage(deviceNumber, command, data, messageId);
                obj.MessageType = Zaber.MessageType.Response;
            else
                for (i = 6:-1:3)
                    data = bitshift(data, 8) + int32(aBytes(i));
                end
                
                obj = Zaber.BinaryMessage(deviceNumber, command, data);
                obj.MessageType = Zaber.MessageType.Response;
            end
        end        
    end
    
end