//*****************************************************************************
//
// File name: McomModbusSlave.cs
// Location: See Matrox Example Launcher in the MIL Control Center
// 
//
// Synopsis:  This program allocates a MIL application and system.
//            Then allocate a MIL industrial communication context to a
//            Modbus slave instance.
//
// Notes:     This example is only available if you have the MIL Industrial Communication package,
//            or another relevant update installed.
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
// All Rights Reserved
//*****************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using Matrox.MatroxImagingLibrary;
using System.Runtime.InteropServices;

namespace MAppStart
{
   class Program
   {
      static MIL_ID MilApplication = MIL.M_NULL;     // Application identifier.
      static MIL_ID MilSystem = MIL.M_NULL;          // System identifier.
      static MIL_ID MilCom = MIL.M_NULL;         // Display identifier.

      /* Control register */
      static byte Trigger;
      static byte ResultACK;
      static byte value = 0;

      static void Main(string[] args)
      {
         byte processingResult;

         /* Allocate a default MIL application, system, display and image. */
         MIL.MappAllocDefault(MIL.M_DEFAULT, ref MilApplication, ref MilSystem, MIL.M_NULL, MIL.M_NULL, MIL.M_NULL);

         MIL.McomAlloc(MilSystem, MIL.M_COM_PROTOCOL_MODBUS, "M_DEFAULT", MIL.M_DEFAULT, MIL.M_DEFAULT, ref MilCom);

         /* Wait for a key press. */
         Console.Write("Waiting for PLC trigger to happen.\nPress <Enter> to end.\n");

         /* Set the data to the initial values. */
         SetInitialStatus();

         while (!Console.KeyAvailable)
         {
            /* Wait that the PLC set the trigger bit */
            if (!WaitForTriggerFromPLC())
            {
               /* Do the requested processing */
               processingResult = DoProcessing();

               /* Write the result back to the PLC*/
               WriteResultToPLC(processingResult);
            }
         }

         SetInitialStatus();

         /* Free MIL objects*/
         MIL.McomFree(MilCom);
         MIL.MappFreeDefault(MilApplication, MilSystem, MIL.M_NULL, MIL.M_NULL, MIL.M_NULL);
      }

      static void WriteStatus(byte triggerACK, byte resultReady, byte resultValue)
      {
         byte[] status = new byte[2];

         status[0] = (byte)(((resultReady & 0x01) << 1) | (triggerACK & 0x01));
         status[1] = resultValue;

         /* Write in the input module slot id 1 as specified in PLC configuration */
         MIL.McomWrite(MilCom, MIL.M_INPUT_REGISTER, 0, 1, status);
      }

      static void ReadControl(ref byte result)
      {
         byte[] control = new byte[2];

         /* Read in the output module slot id 2 as specified in PLC configuration */
         MIL.McomRead(MilCom, MIL.M_HOLDING_REGISTER, 0, 1, control);

         Trigger = (byte)(control[0] & 0x1);
         ResultACK = (byte)((control[0] & 0x2) >> 1);
         result = control[1];
      }

      static void SetInitialStatus()
      {
         WriteStatus(0, 0, 0);
      }

      static bool WaitForTriggerFromPLC()
      {
         bool KeyHit;
         byte ResultValue = 0;

         do
         {
            /* Wait for the trigger from the PLC */
            ReadControl(ref ResultValue);
            Thread.Sleep(10);
            KeyHit = Console.KeyAvailable;
         } while (Trigger == 0 && !KeyHit);

         if (!KeyHit)
         {
            Console.Write("Received Trigger!\n");

            /* Set the TriggerACK and reset ResultReady */
            WriteStatus(1, 0, 0);
         }

         return KeyHit;
      }

      static byte DoProcessing()
      {
         /* Do what need to be done when PLC send the trigger. */
         value++;
         return value;
      }

      static void WriteResultToPLC(byte result)
      {
         bool KeyHit;
         byte ResultCopy = 0;

         /* Set the result value */
         WriteStatus(0, 1, result);

         /* Wait that the PLC acknowledge the result */
         do
         {
            ReadControl(ref ResultCopy);
            Thread.Sleep(10);
            KeyHit = Console.KeyAvailable;
         } while (ResultACK == 0 && !KeyHit);

         if (!KeyHit)
         {
            Console.Write(String.Format("Received result ACK! Value:{0} Copy:{1}\n", value, ResultCopy));

            /* Set the result value */
            WriteStatus(0, 0, ResultCopy);
         }
      }
   }
}