//*****************************************************************************
//
// File name: McomCclinkSlave.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
//            CC-Link IE Field Basic 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.Threading;

using Matrox.MatroxImagingLibrary;
using McomCclinkSlave;

namespace MAppStart
{
   class Program
   {
      const int _toPLCDataRegister = 0;
      const int _fromPLCDataRegister = 0;

      const int _fromPLCTriggerFlag = 0;
      const int _fromPLCAcknowledgeFlag = 1;
      const int _toPLCDataReadyFlag = 0;

      static ushort _internalProcessingValue = 0;

      static void Main(string[] args)
      {
         MIL_ID _milApplication = MIL.M_NULL;    // Application identifier.
         MIL_ID _milSystem = MIL.M_NULL;         // System identifier.
         MIL_ID _milCom = MIL.M_NULL;            // Display identifier.

         ushort 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_CCLINK, "M_DEFAULT", MIL.M_DEFAULT, MIL.M_DEFAULT, ref _milCom);

         /* Create a CC-Link Slave */
         var slave = new CclinkSlave(_milCom);

         if (slave.IsSlaveStopped)
         {
            Console.Write("Slave stopped. Press <Enter> to end.\n");
            goto abort;
         }

         /* Wait for a key press. */
         Console.Write("Press <Enter> to end.\n");

         /* Set the data to the initial values. */
         slave.ClearFlag(deviceNumber: _toPLCDataReadyFlag);
         slave.WriteRegister(deviceNumber: _toPLCDataRegister, value: 0);

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

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

         /* Set the data to the initial values. */
         slave.ClearFlag(deviceNumber: _toPLCDataReadyFlag);
         slave.WriteRegister(deviceNumber: _toPLCDataRegister, value: 0);

 abort:
         /* Free MIL objects*/
         MIL.McomFree(_milCom);
         MIL.MappFreeDefault(_milApplication, _milSystem, MIL.M_NULL, MIL.M_NULL, MIL.M_NULL);
      }

      static bool WaitForTriggerFromPLC(CclinkSlave slave)
      {
         bool _KeyHit;
         bool _isTriggerSet = false;

         Console.Write("Waiting for trigger from PLC!\n");

         do
         {
            /* Wait for the trigger from the PLC */
            _isTriggerSet = slave.ReadFlag(deviceNumber: _fromPLCTriggerFlag);
            Thread.Sleep(10);
            _KeyHit = Console.KeyAvailable;
         } while (_isTriggerSet == false && !_KeyHit);

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

         return _KeyHit;
      }

      static ushort DoProcessing()
      {
         /* Do what need to be done when PLC send the trigger. */
         Console.Write("Computing new data value\n");
         _internalProcessingValue++;
         return _internalProcessingValue;
      }

      static void WriteResultToPLC(CclinkSlave slave, ushort result)
      {
         bool _KeyHit;
         bool _isResultRead = false;
         ushort valueFromPLC = 0;

         /* Set the result value and signal PLC*/
         slave.WriteRegister(deviceNumber: _toPLCDataRegister, value: result);
         slave.SetFlag(deviceNumber: _toPLCDataReadyFlag);

         /* Wait that the PLC acknowledge the result */
         Console.Write("Waiting for PLC acknowledge!\n");
         do
         {
            _isResultRead = slave.ReadFlag(deviceNumber: _fromPLCAcknowledgeFlag);
            Thread.Sleep(10);
            _KeyHit = Console.KeyAvailable;
         } while (_isResultRead == false && !_KeyHit);

         if (_KeyHit)
         {
            valueFromPLC = slave.ReadRegister(deviceNumber: _fromPLCDataRegister);

            Console.Write(string.Format("Received result ACK! Value:{0} CopyBack:{1}\n", result, valueFromPLC));

            /* reset our signal to PLC */
            slave.ClearFlag(deviceNumber: _toPLCDataReadyFlag);
         }
      }
   }
}