public synchronized void process(final Connection connection) throws Exception
    {
        // Determine which tags are to read and which to write
        final MessageRouterProtocol[] readwrite = new MessageRouterProtocol[tags.size()];
        for (int i=0; i<tags.size(); ++i)
        {
            final Tag tag = tags.get(i);
            synchronized (tag)
            {
                switch (tag.getState())
                {
                case READING:
                    readwrite[i] = new MRChipReadProtocol(tag.getName());
                    break;
                default:
                    readwrite[i] = new MRChipWriteProtocol(tag.getName(), tag.getValue());
                    tag.setState(State.WRITING);
                }
            }
        }
        
        // Perform the protocol exchange
        final Encapsulation encap =
            new Encapsulation(SendRRData, connection.getSession(),
                new SendRRDataProtocol(
                    new UnconnectedSendProtocol(connection.getSlot(),
                        new MessageRouterProtocol(CNService.CIP_MultiRequest, MessageRouter(),
                            new CIPMultiRequestProtocol(readwrite)))));
        connection.execute(encap);
        
        // Handle responses: Fetch data, reset 'write' flags
        for (int i=0; i<tags.size(); ++i)
        {
            final Tag tag = tags.get(i);
            synchronized (tag)
            {
                switch (tag.getState())
                {
                case READING:
                    // Read, update the tag's value
                    final MRChipReadProtocol reader = (MRChipReadProtocol)readwrite[i];
                    tag.setValue(reader.getData());
                    logger.log(Level.FINE, "Read {0}", tag);
                    break;
                case WRITING:
                    // Finished writing, return to reading
                    tag.setState(State.READING);
                    logger.log(Level.FINE, "Wrote {0}", tag);
                    break;
                case TO_BE_WRITTEN:
                    // Received yet another value to be written while writing the
                    // previous one, so keep the TO_BE_WRITTEN state
                    logger.log(Level.FINE, "Wrote {0}, need to write updated value", tag);
                }
            }
        }
    }