

package at.acdp.urweb.fhpp;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.*;
import com.digitalpetri.modbus.responses.ModbusResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FHMaster {
    public static int posfaktorNum=1;
    public static int posfaktorDiv=1;



    public static void main(String[] args) {
        try {
            new FHMaster().start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final List<ModbusTcpMaster> masters = new CopyOnWriteArrayList<>();
    private volatile boolean started = false;
    public FHMaster() {
    }

    public void start() throws InterruptedException {
        started = true;
        ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("10.0.31.14")
                .setPort(502)
                .build();
        ModbusTcpMaster master = new ModbusTcpMaster(config);
        var f=master.connect()
                .whenComplete((result, ex) -> bereitschaft(master))
                .whenComplete((result, ex) -> direktAuftrag(master));



        while(true) {
            Thread.sleep(1000);
            readStatus(master);
        }
    }

    private void sendRequests(ModbusTcpMaster master, List<ModbusRequest> reqs) {
        for(var req: reqs) {
            var future=master.sendRequest(req, 0);
            future.whenCompleteAsync((response, ex) -> {
                if (response != null) {
                    ReferenceCountUtil.release(response);
                }
                else {
                    logger.error("Completed exceptionally, message={}", ex.getMessage(), ex);
                }
            }, Modbus.sharedExecutor());
        }
    }

    void direktAuftrag(ModbusTcpMaster master) {
        byte ccon=0x0;
        ccon|=(1<<0); //Enable drive
        ccon|=(0<<1); //Stop
        ccon|=(1<<2); //Release Brake
        ccon|=(0<<3); //Reset fault
        ccon|=(0<<4); //Reserved
        ccon|=(0<<5); //lock software
        ccon|=(0<<6 | 1 <<7); //lock software
                            // 0,0 Satzselektion
                            // 1,0 Direktauftrag
                            // 0,1 Reserviert
                            // 1,1 Reserviert

        byte cpos=0x0;
        ccon|=(1<<0); // !Halt
        ccon|=(0<<1); // start fahrauftrag
        ccon|=(1<<2); // start homing
        ccon|=(0<<3); // jog pos
        ccon|=(0<<4); // jog neg
        ccon|=(0<<5); //  teach value
        ccon|=(0<<6); //  teach value
        ccon|=(0<<7); //  teach value

        byte cdir=0x0;
        cdir|=(1<<0); //absolute/relative
        cdir|= (1<<1| 1<<2); //absolute/relative
                                // 0,0 Positionsregelung
                                // 0,1 Kraftbetrieb
                                // 1,0 Geschwindigkeitsregelung
                                // 1,1 Reserviert
        cdir|=(1<<3);   // Function Number Kurvenscheibenfunktion (0=nein)
        cdir|=(1<<5);   // Function Group Kurvenscheibenfunktion (0=nein)
        cdir|=(1<<7);   // 0 = normal; 1=kurvenscheibenfunktion

        byte sbyte4=100; // Geschw, in % vom Basiswert (PNU540
        int pos=50; //position in positionseinheit;
        byte sbyte5= (byte)(pos& 0xff);
        byte sbyte6= (byte)(pos>>8 & 0xff);
        byte sbyte7= (byte)(pos>>16 & 0xff);
        byte sbyte8= (byte)(pos>>24 & 0xff);

        CompletableFuture<ReadHoldingRegistersResponse> f =
                master.sendRequest(new WriteMultipleRegistersRequest(0,4, new byte[]{
                        ccon,
                        cpos,
                        cdir,
                        sbyte4,
                        sbyte5,
                        sbyte6,
                        sbyte7,
                        sbyte8}), 0);
    }

    void readStatus(ModbusTcpMaster master) {
        CompletableFuture<ReadHoldingRegistersResponse> f =master.sendRequest(new ReadHoldingRegistersRequest(0,4), 0);
        f.whenCompleteAsync((response, ex) -> {
            if (response != null) {
                var bb=response.getRegisters();
                try {

                    System.out.println(ByteBufUtil.hexDump(bb));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
                ReferenceCountUtil.release(response);
            }
            else {
                logger.error("Completed exceptionally, message={}", ex.getMessage(), ex);
            }
        }, Modbus.sharedExecutor());
    }

    private void bereitschaft(ModbusTcpMaster master) {
        List<ModbusRequest> blist = new ArrayList<>();
        blist.add(new WriteMultipleRegistersRequest(0,4, new byte[]{0x01,0,0,0,0,0,0,0}));
        blist.add(new WriteMultipleRegistersRequest(0,4, new byte[]{0x11,0,0,0,0,0,0,0}));
        blist.add(new WriteMultipleRegistersRequest(0,4, new byte[]{0x01,0,0,0,0,0,0,0}));
        sendRequests(master, blist);
    }

    public void stop() {
        started = false;
        masters.forEach(ModbusTcpMaster::disconnect);
        masters.clear();
    }

}