function DRLCframe = DRLCFrameGenerator(cfg)
%DRLCFRAMEGENERATOR Generate ZigBee Smart Energy frames of the DRLC cluster
%   DRLCFRAME = DRLCFRAMEGENERATOR(CFG) generates the Demand Response Load
%   Control frame DRLCFRAME corresponding to the configuration object CFG.
%
%   See also zigbee.DRLCFrameConfig, zigbee.DRLCFrameDecoder.

%   Copyright 2017-2023 The MathWorks, Inc.

switch cfg.CommandType
  case 'Load Control Event'
    DRLCframe = generateLoadControlEventFrame(cfg);
    
  case 'Cancel Load Control Event'
    DRLCframe = generateCancelLoadControlEventFrame(cfg);
    
  case 'Cancel All Load Control Events'
    DRLCframe = generateCancelAllLoadControlEventsFrame(cfg);
    
  case 'Report Event Status'
    DRLCframe = generateReportEventStatusFrame(cfg);
end

DRLCframe = zigbee.internal.bits2bytes(DRLCframe); % Convert bits to bytes
end

function frame = generateLoadControlEventFrame(cfg)

% 1. Issuer Event ID (4-octets)
issuerEventID = int2bit(hex2dec(cfg.EventID), 4*8, false);

% 2. Device Class Bitmap (2-octets)
deviceClass = generateDeviceClassBitmap(cfg);

% 3. Utility Enrollment Group (1-octet)
DeviceGroup = int2bit(hex2dec(cfg.DeviceGroup), 1*8, false);

% 4. Start Time (4-octets)
Time = int2bit(cfg.Time, 4*8, false);

% 5. Duration In Minutes (2-octets)
Duration = int2bit(cfg.Duration, 2*8, false);

% 6. Criticality Level (1-octet)
criticalityLevel = generateCriticalityLevel(cfg);

% 7. Cooling Temperature Offset (1-octet)
CoolingOffset = int2bit(cfg.CoolingOffset*10, 1*8, false);

% 8. Heating Temperature Offset (1-octet)
HeatingOffset = int2bit(cfg.HeatingOffset*10, 1*8, false);

% 9. Cooling Temperature Set-Point (2-octets)
CoolingSetPoint = ...
    int2bit(typecast(int16(cfg.CoolingSetPoint*100), 'uint16'), 2*8, false);

% 10. Heating Temperature Set-Point (2-octets)
HeatingSetPoint = ...
    int2bit(typecast(int16(cfg.HeatingSetPoint*100), 'uint16'), 2*8, false);

% 11. Average Load Adjustment Percentage (1-octet)
notUsed = '80';
averageLoadAdjustmentPercentage = int2bit(hex2dec(notUsed), 8, false);

% 12. Duty Cycle (1-octet)
notUsed = 'FF';
dutyCycle = int2bit(hex2dec(notUsed), 1*8, false);

% 13. Event Control Bitmap (1-octet)
EventControl = generateEventControl(cfg);

% 14. Put it all together
frame = [issuerEventID; deviceClass; DeviceGroup; ...
    Time; Duration; criticalityLevel; CoolingOffset; ...
    HeatingOffset; CoolingSetPoint; ...
    HeatingSetPoint; averageLoadAdjustmentPercentage; dutyCycle; ...
    EventControl];

end

function frame = generateCancelLoadControlEventFrame(cfg)

% 1. Issuer Event ID (4-octets)
issuerEventID = int2bit(hex2dec(cfg.EventID), 4*8, false);

% 2. Device Class Bitmap (2-octets)
deviceClass = generateDeviceClassBitmap(cfg);

% 3. Utility Enrollment Group (1-octet)
DeviceGroup = int2bit(hex2dec(cfg.DeviceGroup), 1*8, false);

% 4. Cancel Control Bitmap (1-octet)
cancelControl = int2bit(double(cfg.RandomEnd), 1*8, false);

% 5. Effective Time (4-octets)
effectiveTime = int2bit(cfg.Time, 4*8, false);

% 6. Put it all together
frame = [issuerEventID; deviceClass; DeviceGroup; ...
        cancelControl; effectiveTime];

end

function frame = generateCancelAllLoadControlEventsFrame(cfg)

% Only one field is present in this frame

% 1. Cancel Control Bitmap (1-octet)
cancelControl = int2bit(double(cfg.RandomEnd), 1*8, false);

% construct the frame
frame = cancelControl;

end

function frame = generateReportEventStatusFrame(cfg)

% 1. Issuer Event ID (4-octets)
issuerEventID      = int2bit(hex2dec(cfg.EventID), 4*8, false);

% 2. Event Status (1-octet)
eventStatus        = generateEventStatus(cfg);

% 3. Event Status Time (4-octets)
eventStatusTime    = int2bit(cfg.Time, 4*8, false);

% 4. Criticality Level Applied (1-octet)
criticalityLevel   = generateCriticalityLevel(cfg);

% 5. Cooling Temperature Set-Point Applied (2-octets)
CoolingSetPoint = ...
    int2bit(typecast(int16(cfg.CoolingSetPoint*100), 'uint16'), 2*8, false);

% 6. Heating Temperature Set-Point Applied (2-octets)
HeatingSetPoint = ...
    int2bit(typecast(int16(cfg.HeatingSetPoint*100), 'uint16'), 2*8, false);

% 7. Average Load Adjustment Percentage Applied (1-octet)
notUsed = '80';
averageLoadAdjustmentPercentage = int2bit(hex2dec(notUsed), 8, false);

% 8. Duty Cycle (1-octet)
notUsed = 'FF';
dutyCycle = int2bit(hex2dec(notUsed), 1*8, false);

% 9. Event Control Bitmap (1-octet)
eventControl = generateEventControl(cfg);

% 10. SignatureType (1-octet)
signatureType = generateSignatureType(cfg);

% 11. Signature (43-octets)
signature = ones(42*8, 1); % not used

% 12. Put it all together
frame = [issuerEventID; eventStatus; eventStatusTime; criticalityLevel;...
        CoolingSetPoint; HeatingSetPoint; ...
        averageLoadAdjustmentPercentage; dutyCycle; eventControl; ...
        signatureType; signature];

end

function deviceClass = generateDeviceClassBitmap(cfg)

deviceClass = zeros(16, 1);
index = strcmp(zigbee.DRLCFrameConfig.DeviceClassValues, cfg.DeviceClass);
deviceClass(index) = 1;
end

function criticalityLevel = generateCriticalityLevel(cfg)

index = find(strcmp(zigbee.DRLCFrameConfig.CriticalityLevelValues, cfg.CriticalityLevel));
criticalityLevel = int2bit(index, 1*8, false);
end

function eventControl = generateEventControl(cfg)

eventControl = zeros(8, 1);

% Bit-0 : Randomize Start Time
eventControl(1) = double(cfg.RandomStart);

% Bit-1 : Randomize End Time
eventControl(2) = double(cfg.RandomEnd);

% Bit-2 to Bit-7 : Reserved (6 bits)
end

function eventStatus = generateEventStatus(cfg)

index = find(strcmp(zigbee.DRLCFrameConfig.EventStatusValues, cfg.EventStatus));
eventStatus = int2bit(index, 1*8, false);
end

function signatureType = generateSignatureType(cfg)

switch cfg.SignatureType
  case 'No Signature'
    signatureType = int2bit(0, 1*8, false);
  case 'ECDSA'
    signatureType = int2bit(1, 1*8, false);
end

end
