ADO Adapter works locally but fails on other machine

I developed an ADO adapter that integrates with my custom TCP server built using Node.js. It runs correctly on my personal machine. However, after copying the ADO adapter to a university machine, it no longer functions as expected.

On the university setup, the log displays the message:
“Initiating {0} {1} based connection”,
but the connection doesn’t seem to proceed beyond this point. It’s also can’t connect to PMU Connection tester. ‘Attempting to connect’.

Additionally, when I try to export measurements using the OpenPDC web interface at the university, it doesn’t work either—I see no data in the “Trend Data Chart.”
Interestingly, OpenHistorian does show that data is being received.

Could this be a configuration or environment issue? What might be causing the adapter to hang at that connection message, and why isn’t the data visible in OpenPDC but is present in OpenHistorian?
My code:

using System;
using System.IO;
using System.Text;
using GSF;
using GSF.PhasorProtocols;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using WebSocketSharp;
using WebSocketSharp.Server;

namespace DeviceToData
{
  public class DotEnv
{
  public static void Load(string filePath = ".env")
{
  if (!File.Exists(filePath))
{
  Console.WriteLine(string.Format("Warning: {0} file not found. Using system environment variables.", filePath));
  return;
}

  foreach (var line in File.ReadAllLines(filePath))
  {
    var trimmedLine = line.Trim();

    // Skip empty lines and comments
    if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith("#"))
      continue;

    var equalIndex = trimmedLine.IndexOf('=');
    if (equalIndex == -1)
      continue;

    var key = trimmedLine.Substring(0, equalIndex).Trim();
    var value = trimmedLine.Substring(equalIndex + 1).Trim();

    // Remove quotes if present
    if (value.StartsWith("\"") && value.EndsWith("\""))
      value = value.Substring(1, value.Length - 2);
    else if (value.StartsWith("'") && value.EndsWith("'"))
      value = value.Substring(1, value.Length - 2);

    // Only set if not already set in system environment
    if (Environment.GetEnvironmentVariable(key) == null)
    {
      Environment.SetEnvironmentVariable(key, value);
    }
  }
}
}

  public class PhasorDataBehavior : WebSocketBehavior
  {
    public void SendPhasorData(object data)
    {
      if (Context.WebSocket.ReadyState == WebSocketState.Open)
      {
        string json = JsonConvert.SerializeObject(data);
        Send(json);
      }
    }
  }

  class Program
  {
    static MultiProtocolFrameParser parser;
    static long frameCount;
    static WebSocketServer wsServer;

    static void Main(string[] args)
  {
    // Load environment variables from .env file
    DotEnv.Load();

    string wsHost = Environment.GetEnvironmentVariable("WS_HOST");
    string wsPort = Environment.GetEnvironmentVariable("WS_PORT");


    wsServer = new WebSocketServer(string.Format("ws://{0}:{1}", wsHost, wsPort));
    wsServer.AddWebSocketService<PhasorDataBehavior>("/phasors");
    wsServer.Start();


    Console.WriteLine("WebSocket server started on ws://{0}:{1}/phasors", wsHost, wsPort);

    parser = new MultiProtocolFrameParser();

    parser.ConnectionAttempt += Parser_ConnectionAttempt;
    parser.ConnectionEstablished += Parser_ConnectionEstablished;
    parser.ConnectionException += Parser_ConnectionException;
    parser.ParsingException += Parser_ParsingException;
    parser.ReceivedConfigurationFrame += Parser_ReceivedConfigurationFrame;
    parser.ReceivedDataFrame += Parser_ReceivedDataFrame;



    string pmuCommProtocol = Environment.GetEnvironmentVariable("PMU_COMM_PROTOCOL");
    string pmuID = Environment.GetEnvironmentVariable("PMU_ID");
    string pmuTransProtocol = Environment.GetEnvironmentVariable("PMU_TRANS_PROTOCOL");
    string pmuServer = Environment.GetEnvironmentVariable("PMU_SERVER");
    string pmuHost = Environment.GetEnvironmentVariable("PMU_PORT");




    parser.ConnectionString = string.Format("phasorProtocol={0}; accessID={1}; transportprotocol={2}; server={3}; port={4}; islistener=false;", pmuCommProtocol, pmuID, pmuTransProtocol, pmuServer, pmuHost);

    parser.AutoRepeatCapturedPlayback = true;
    parser.AutoStartDataParsingSequence = true;
    parser.Start();

    Console.WriteLine("Press Enter to exit...");
    Console.ReadLine();

    wsServer.Stop();
  }

  private static string GetConnectionString()
  {
    // Get db credentials from env variables
    string server = Environment.GetEnvironmentVariable("MYSQL_HOST");
    string user = Environment.GetEnvironmentVariable("MYSQL_USER");
    string password = Environment.GetEnvironmentVariable("MYSQL_PASSWORD");
    string database = Environment.GetEnvironmentVariable("MYSQL_DATABASE");

    // Validate required env variables
    if (string.IsNullOrEmpty(password))
    {
      throw new InvalidOperationException("MYSQL_PASSWORD environment variable is required");
    }

    return string.Format("server={0};user={1};password={2};database={3}", server, user, password, database);
  }

  private static void Parser_ReceivedDataFrame(object sender, EventArgs<IDataFrame> e)
  {
    frameCount++;

    if (e.Argument.Cells.Count > 0)
    {
      Console.WriteLine(string.Format("Received Data Frame #{0} with {1} devices", frameCount, e.Argument.Cells.Count));

      string connStr = GetConnectionString();

      using (MySqlConnection conn = new MySqlConnection(connStr))
      {
        try
        {
          conn.Open();

          string sql = "INSERT INTO timeseriesmeasurement (timestamp, frequency, phasor_angle, phasor_magnitude, id_code, definition, phasor_type) " +
          "VALUES (@timestamp, @frequency, @angle, @magnitude, @idcode, @definition, @type)";

          foreach (IDataCell device in e.Argument.Cells)
          {
            Console.WriteLine(string.Format("Anton Phasors (Total Devices): {0}", e.Argument.Cells.Count));

            foreach (var phasor in device.PhasorValues)
            {
              var dataToSend = new
              {
                Timestamp = e.Argument.Timestamp.ToString("o"),
                Frequency = device.FrequencyValue.Frequency,
                PhasorAngle = phasor.Angle,
                PhasorMagnitude = phasor.Magnitude,
                DeviceID = device.IDCode,
                PhasorLabel = phasor.Definition.Label,
                PhasorType = phasor.Type,
              };

              wsServer.WebSocketServices["/phasors"].Sessions.Broadcast(JsonConvert.SerializeObject(dataToSend));
              Console.WriteLine("data sent to WebSocket clients");

              using (MySqlCommand cmd = new MySqlCommand(sql, conn))
              {
                string formattedTimestamp = ((DateTime)e.Argument.Timestamp).ToString("yyyy-MM-dd HH:mm:ss.fff");

                cmd.Parameters.AddWithValue("@timestamp", formattedTimestamp);
                cmd.Parameters.AddWithValue("@frequency", Convert.ToDouble(device.FrequencyValue.Frequency));
                cmd.Parameters.AddWithValue("@angle", Convert.ToDouble(phasor.Angle));
                cmd.Parameters.AddWithValue("@magnitude", Convert.ToDouble(phasor.Magnitude));
                cmd.Parameters.AddWithValue("@idcode", Convert.ToInt32(device.IDCode));
                cmd.Parameters.AddWithValue("@definition", phasor.Definition);
                cmd.Parameters.AddWithValue("@type", phasor.Type.ToString());

                Console.WriteLine(string.Format("Phasor TYPE: {0}", phasor.Type));


                cmd.ExecuteNonQuery();
              }
            }
          }
        }
        catch (Exception ex)
        {
          Console.WriteLine(string.Format("MySQL Error: {0}", ex.Message));
        }
      }
    }
  }

  private static void Parser_ConnectionAttempt(object sender, EventArgs e)
  {
    Console.WriteLine("Attempting connection...");
  }

  private static void Parser_ConnectionEstablished(object sender, EventArgs e)
  {
    Console.WriteLine(string.Format("Initiating {0} {1} based connection...", parser.PhasorProtocol.GetFormattedProtocolName(), parser.TransportProtocol.ToString().ToUpper()));
  }

  private static void Parser_ConnectionException(object sender, EventArgs<Exception, int> e)
  {
    Console.WriteLine(string.Format("Connection attempt {0} failed due to exception: {1}", e.Argument2, e.Argument1));
  }

  private static void Parser_ParsingException(object sender, EventArgs<Exception> e)
  {
    Console.WriteLine(string.Format("Parsing exception: {0}", e.Argument));
  }

  private static void Parser_ReceivedConfigurationFrame(object sender, EventArgs<IConfigurationFrame> e)
  {
    Console.WriteLine(string.Format("Received configuration frame with {0} device(s)", e.Argument.Cells.Count));
  }
}
}

If you are using SQL Server, it has to be configured to support remote IP connections.

MultiProtocolFrameParser.ConnectionEstablished is invoked after successfully connecting to the PMU. If it doesn’t show anything after that, it means the PMU is not sending any data. It sounds like your PMU is simply not working correctly, or maybe you are connecting to another device that isn’t a PMU.

This seems like an entirely different problem. PMU Connection Tester displays “Attempting to connect” before it establishes the connection to the PMU. If it’s stuck there, then I’d expect it should eventually time out within 30 seconds if you leave it alone.

It’s hard to pin down an exact cause, especially since you seem to be having two different problems between MultiProtocolFrameParser and PMU Connection Tester. We’d need more consistent information in order to point you in any particular direction. As for openHistorian, We don’t know anything about your setup so we can’t even begin to speculate about what data it’s receiving and from what device.