#!/usr/bin/env python3
"""Synthetic function-06 frames only. No sockets, PLC access, or dependencies."""
import struct


def frame(address, value, transaction=1):
    pdu = struct.pack('>BHH', 6, address, value)
    return struct.pack('>HHHB', transaction, 0, len(pdu) + 1, 1) + pdu


def decode(raw):
    if len(raw) != 12:
        raise ValueError('this lab accepts only 12-byte function-06 requests')
    transaction, protocol, length, unit, function, address, value = struct.unpack('>HHHBBHH', raw)
    if protocol != 0 or length != len(raw) - 6 or function != 6:
        raise ValueError('invalid protocol, length, or function')
    return transaction, unit, address, value


def authorize(unit, address, value, role, mode):
    # An illustrative out-of-band policy, NOT a Modbus protocol feature.
    return (unit == 1 and address == 100 and role == 'engineer'
            and mode == 'maintenance' and 400 <= value <= 600)


def main():
    for name, raw, role, mode in [
        ('approved', frame(100, 500), 'engineer', 'maintenance'),
        ('wrong role', frame(100, 500), 'viewer', 'maintenance'),
        ('wrong mode', frame(100, 500), 'engineer', 'production'),
        ('out of envelope', frame(100, 900), 'engineer', 'maintenance'),
        ('wrong register', frame(101, 500), 'engineer', 'maintenance'),
    ]:
        tx, unit, address, value = decode(raw)
        allowed = authorize(unit, address, value, role, mode)
        print(f'{name}: syntax=valid tx={tx} address={address} value={value} authorized={allowed}')
        assert allowed == (name == 'approved')
    raw = frame(100, 500)
    print('frame:', raw.hex(' '))
    for bad in (raw[:-1], raw[:4] + b'\x00\x07' + raw[6:], raw[:7] + b'\x03' + raw[8:]):
        try:
            decode(bad)
        except ValueError:
            continue
        raise AssertionError('malformed input accepted')
    print('malformed frames: rejected (3/3)')


if __name__ == '__main__':
    main()
