@Override
    protected void decode(ConversationContext<TPKTPacket> context, TPKTPacket msg) throws Exception {
        
        final S7Message s7msg = msg.getPayload().getPayload();
        final S7Parameter parameter = s7msg.getParameter();
        final S7PayloadUserData payload = (S7PayloadUserData) s7msg.getPayload();       
        
        if (parameter instanceof S7ParameterModeTransition) {  //(01)  
            
            S7ModeEvent modeEvent = new S7ModeEvent((S7ParameterModeTransition) parameter);
            eventQueue.add(modeEvent);
            
        } else if (parameter instanceof S7ParameterUserData) {
            
            S7ParameterUserData parameterUD = (S7ParameterUserData) parameter;
            List<S7ParameterUserDataItem> parameterUDItems = parameterUD.getItems();
            
            for (S7ParameterUserDataItem parameterUDItem : parameterUDItems) {
                
                if (parameterUDItem instanceof S7ParameterUserDataItemCPUFunctions) {
                    
                    S7ParameterUserDataItemCPUFunctions myParameter = (S7ParameterUserDataItemCPUFunctions) parameterUDItem;
                    
                    if ((myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x03)) { //(02)                      
                        
                        payload.getItems().forEach(item ->{
                            if (item instanceof S7PayloadDiagnosticMessage) {
                                final S7PayloadDiagnosticMessage pload = (S7PayloadDiagnosticMessage) item; 
                                if ((pload.getEventId() >= 0x0A000) & (pload.getEventId() <= 0x0BFFF)) {
                                    S7UserEvent userEvent = S7UserEvent.of(pload);
                                    eventQueue.add(userEvent);                                
                                } else {
                                    S7SysEvent sysEvent = S7SysEvent.of(pload);
                                    eventQueue.add(sysEvent);                                        
                                }
                            } 
                        });

                    } else if ((myParameter.getCpuFunctionType() == 0x00) &&
                        ((myParameter.getCpuSubfunction() == 0x05) ||
                            (myParameter.getCpuSubfunction() == 0x06) ||
                            (myParameter.getCpuSubfunction() == 0x0c) ||
                            (myParameter.getCpuSubfunction() == 0x11) ||
                            (myParameter.getCpuSubfunction() == 0x12) ||
                            (myParameter.getCpuSubfunction() == 0x13) ||
                            (myParameter.getCpuSubfunction() == 0x16))) { //(04)
                        
                        payload.getItems().forEach(item ->{
                            S7AlarmEvent alrmEvent = S7AlarmEvent.of(item);
                            eventQueue.add(alrmEvent);                                
                        });
                        
                                                
                    } else if ((myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x13)) {
                        //TODO: Requires reverse engineering.
                    } else if (((myParameter.getCpuFunctionGroup() == 0x02) && (myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x01))) { //(05)

                        S7ParameterUserDataItemCPUFunctions parameterItem =
                            (S7ParameterUserDataItemCPUFunctions)
                                ((S7ParameterUserData) parameter).getItems().get(0);

                        S7PayloadUserDataItemCyclicServicesPush payloadItem =
                            (S7PayloadUserDataItemCyclicServicesPush)
                                payload.getItems().get(0);

                        S7CyclicEvent cycEvent = new S7CyclicEvent(cycRequests.get(parameterItem.getSequenceNumber()),
                            parameterItem.getSequenceNumber(),
                            payloadItem);
                        
                        if (cycChangeValueEvents.containsKey(parameterItem.getSequenceNumber())){
                            S7CyclicEvent lastCycEvent = cycChangeValueEvents.get(parameterItem.getSequenceNumber());
                            if (!cycEvent.equals(lastCycEvent)) {
                                cycChangeValueEvents.replace(parameterItem.getSequenceNumber(), cycEvent);
                                eventQueue.add(cycEvent);                                
                            }
                            
                        } else {
                            eventQueue.add(cycEvent);
                        }

                    } else if ((myParameter.getCpuFunctionGroup() == 0x02) && (myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x05)) { //(06)
                        
                        S7ParameterUserDataItemCPUFunctions parameterItem =
                            (S7ParameterUserDataItemCPUFunctions)
                                ((S7ParameterUserData) parameter).getItems().get(0);

                        S7PayloadUserDataItemCyclicServicesChangeDrivenPush payloadItem =
                            (S7PayloadUserDataItemCyclicServicesChangeDrivenPush)
                                payload.getItems().get(0);

                        S7CyclicEvent cycEvent = new S7CyclicEvent(null,
                            parameterItem.getSequenceNumber(),
                            payloadItem);
                        eventQueue.add(cycEvent);

                    } else if ((myParameter.getCpuFunctionType() == 0x08) && (myParameter.getCpuSubfunction() == 0x01)) {
                        //TODO: Requires reverse engineering.
                    } else if ((myParameter.getCpuFunctionType() == 0x08) && (myParameter.getCpuSubfunction() == 0x04)) {
                        //TODO: Requires reverse engineering.
                    }
                }
            }
        }
    }
DECODE:
This method is called when there is no handler for the message. 
By default it must correspond to asynchronous events, which if so, 
must be transferred to the event queue.
The event's own information is encapsulated in the parameters and payload
field. From this it is abstracted to the corresponding event model.
01. S7ModeEvent:
02. S7UserEvent:
03. S7SysEvent:
04. S7AlarmEvent
05. S7CyclicEvent:
06. S7CyclicEvent:
TODO: Use mspec to generate types that allow better interpretation of
the code using "instanceof".
plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java