Purge old files

Signed-off-by: mharb <mharb@noreply.localhost>
This commit is contained in:
mharb
2025-08-23 19:41:28 -04:00
committed by mharb
parent d33fadbab5
commit fd1e200919
15 changed files with 0 additions and 629 deletions

View File

@@ -1,69 +0,0 @@
using System;
using Modbus.Device;
using static System.Console;
namespace ProtosXdigitalDemo
{
internal class RegisterReader
{
private readonly ModbusIpMaster _master;
public RegisterReader(ModbusIpMaster master)
{
_master = master;
}
public void ReadAll()
{
WriteLine("Reading all input registers…");
try
{
// Read all input registers as ushorts
ushort[] registers = _master.ReadInputRegisters(0, ChannelCache.AnalogInputCount);
Display(registers);
// Convert to bool[] and display
bool[] bits = ConvertRegistersToBools(registers);
Display(bits);
}
catch (Exception ex)
{
WriteLine($"[Error] Reading input registers failed → {ex.Message}");
PromptKey("Press any key to continue…");
}
}
private void Display(ushort[] registers)
{
WriteLine("Input Registers (ushort):");
for (int i = 0; i < registers.Length; i++)
{
WriteLine($" Register #{i + 1}: {registers[i]}");
}
}
private void Display(bool[] bits)
{
WriteLine("Flattened Bits:");
for (int i = 0; i < bits.Length; i++)
{
WriteLine($" Bit #{i + 1}: {(bits[i] ? "On" : "Off")}");
}
}
// Map a ushort's bits to bools using AND operator
private bool[] ConvertRegistersToBools(ushort[] registers)
{
return registers.SelectMany(r => Enumerable.Range(0, 16)
.Select(bit => ((r >> bit) & 1) == 1))
.ToArray();
}
private void PromptKey(string prompt)
{
WriteLine(prompt);
ReadKey(intercept: true);
}
}
}

View File

@@ -1,16 +0,0 @@
namespace ProtosXdigitalDemo
{
internal static class ChannelCache
{
public static ushort DigitalInputCount { get; private set; }
public static ushort DigitalOutputCount { get; private set; }
public static ushort AnalogInputCount { get; private set; }
public static void Initialize()
{
DigitalInputCount = (ushort)Data.MachineState.digitalInputChannels.Length;
DigitalOutputCount = (ushort)Data.MachineState.digitalOutputChannels.Length;
AnalogInputCount = (ushort)Data.MachineState.analogInputChannels.Length;
}
}
}

View File

@@ -1,35 +0,0 @@
using System;
using static System.Console;
namespace ProtosXdigitalDemo
{
internal class CommandLineParser
{
public string? RecipeName { get; }
public int TimesToRepeat { get; }
private CommandLineParser(string? recipeName, int timesToRepeat)
{
RecipeName = recipeName;
TimesToRepeat = timesToRepeat;
}
public static CommandLineParser Parse(string[] args)
{
return args.Length switch
{
0 => new CommandLineParser(null, 0),
1 => new CommandLineParser(args[0], 0),
2 => new CommandLineParser(args[0], Convert.ToInt32(args[1])),
_ => ExitOnTooManyArgs()
};
}
private static CommandLineParser ExitOnTooManyArgs()
{
WriteLine("Too many command-line arguments; exiting.");
Environment.Exit(Data.MachineState.failureCode[2]);
throw new OperationCanceledException();
}
}
}

View File

@@ -1,17 +0,0 @@
using System;
using static System.Console;
namespace ProtosXdigitalDemo
{
internal static class ConsoleSetup
{
public static void ApplyDefaultAppearance()
{
WindowUtility.SetAppearanceOptions();
WindowUtility.MoveWindowToCenter();
BackgroundColor = ConsoleColor.DarkBlue;
ForegroundColor = ConsoleColor.White;
Clear();
}
}
}

View File

@@ -1,73 +0,0 @@
namespace ProtosXdigitalDemo
{
public static class Data
{
public static class MachineState
{
public static bool[] digitalInputChannels { get; } = new bool[16];
public static bool[] digitalOutputChannels { get; } = new bool[16];
public static double[] analogInputChannels { get; } = new double[8];
public static byte[] failureCode { get; } = new byte[256];
public static ushort digitalInputCount { get; private set; }
public static ushort digitalOutputCount { get; private set; }
public static ushort analogInputCount { get; private set; }
public static void InitializeCounts()
{
digitalInputCount = (ushort)digitalInputChannels.Length;
digitalOutputCount = (ushort)digitalOutputChannels.Length;
analogInputCount = (ushort)analogInputChannels.Length;
}
}
internal enum digitalInputChannels
{
DigitalInput0,
DigitalInput1,
DigitalInput2,
DigitalInput3,
DigitalInput4,
DigitalInput5,
DigitalInput6,
DigitalInput7,
DigitalInput8,
DigitalInput9,
DigitalInput10,
DigitalInput11,
DigitalInput12,
DigitalInput13,
DigitalInput14,
DigitalInput15
}
internal enum digitalOutputChannels
{
KitchenLight1,
KitchenLight2,
KitchenLight3,
PorchLight1,
Porchlight2,
OutsideFloodlight1,
OutsideFloodlight2,
PantryLight1,
FrontRoomLight1,
FrontRoomLight2,
FrontRoomLight3,
Radio,
LandingLight1,
ServerRoomLights,
HallLight1,
HallLight2
}
internal enum analogInputChannels
{
TemperatureSensor1,
TemperatureSensor2
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
using Modbus.Device;
using static System.Console;
namespace ProtosXdigitalDemo
{
internal class InputReader
{
private readonly ModbusIpMaster _master;
public InputReader(ModbusIpMaster master)
{
_master = master;
}
public void ReadAll()
{
WriteLine("Reading all discrete inputs…");
try
{
bool[] inputs = _master.ReadInputs(0, ChannelCache.DigitalInputCount);
Display(inputs);
}
catch (Exception ex)
{
WriteLine($"[Error] Reading inputs failed → {ex.Message}");
PromptKey("Press any key to continue…");
}
}
private void Display(bool[] inputs)
{
WriteLine("Discrete Inputs:");
for (int i = 0; i < inputs.Length; i++)
{
string state = inputs[i] ? "On" : "Off";
WriteLine($" Input #{i + 1}: {state}");
}
}
private void PromptKey(string prompt)
{
WriteLine(prompt);
ReadKey(intercept: true);
}
}
}

View File

@@ -1,14 +0,0 @@

namespace ProtosXdigitalDemo
{
internal class LoadFailureCodes
{
public static void FailureCodeValues()
{
Data.MachineState.failureCode[0] = 0; // success
Data.MachineState.failureCode[1] = 1; // digital write error from hardware
Data.MachineState.failureCode[2] = 2; // too many CLI parameters
Data.MachineState.failureCode[255] = 255; // other error
}
}
}

View File

@@ -1,59 +0,0 @@
using System;
using Modbus.Device;
using static System.Console;
namespace ProtosXdigitalDemo
{
internal class OutputExerciser
{
private readonly ModbusIpMaster _master;
public OutputExerciser(ModbusIpMaster master)
{
_master = master;
}
public void RunSequence()
{
SetAllOutputs(false, "Turning all outputs OFF…");
WaitForKey("Press any key to turn outputs ON…");
SetAllOutputs(true, "Turning all outputs ON…");
WaitForKey("Press any key to turn outputs OFF and finish…");
SetAllOutputs(false, "Turning all outputs OFF…");
}
private void SetAllOutputs(bool state, string message)
{
WriteLine(message);
UpdateMachineState(state);
int result = WriteMachineDigitalStateToIO.WriteMachineStateToIO(_master, 0);
if (result == Data.MachineState.failureCode[0])
{
WriteLine($"All digital outputs are now {(state ? "ON" : "OFF")}");
}
else
{
WriteLine("[Error] Writing machine-state failed");
WaitForKey("Press any key to exit…");
Environment.Exit(Data.MachineState.failureCode[1]);
}
}
private void UpdateMachineState(bool state)
{
for (int i = 0; i < Data.MachineState.digitalOutputChannels.Length; i++)
{
Data.MachineState.digitalOutputChannels[i] = state;
}
}
private void WaitForKey(string prompt)
{
WriteLine(prompt);
ReadKey(intercept: true);
}
}
}

View File

@@ -1,41 +0,0 @@
using System;
using System.Net.Sockets;
using Modbus.Device;
using static System.Console;
namespace ProtosXdigitalDemo
{
public class Program
{
private const string IpAddress = "10.10.1.1";
private const int Port = 502;
public static int Main(string[] args)
{
//ConsoleSetup.ApplyDefaultAppearance();
var cmd = CommandLineParser.Parse(args); // still available
LoadFailureCodes.FailureCodeValues(); // assumed static initializer
try
{
using var tcpClient = new TcpClientConnector(IpAddress, Port).Connect();
var master = ModbusIpMaster.CreateIp(tcpClient);
ChannelCache.Initialize();
new InputReader(master).ReadAll();
new OutputExerciser(master).RunSequence();
}
catch (OperationCanceledException)
{
// an Exit was requested
}
catch (Exception ex)
{
WriteLine($"[Fatal] {ex}");
return Data.MachineState.failureCode[1];
}
return Data.MachineState.failureCode[0];
}
}
}

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="ProtosXdigitalDemo.zip" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NModbus4" Version="2.1.0" />
<PackageReference Include="System.IO.Ports" Version="9.0.7" />
</ItemGroup>
</Project>

View File

@@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36121.58
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtosXdigitalDemo", "ProtosXdigitalDemo.csproj", "{A013091C-203C-48BA-8CD2-317296C4FB44}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A013091C-203C-48BA-8CD2-317296C4FB44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A013091C-203C-48BA-8CD2-317296C4FB44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A013091C-203C-48BA-8CD2-317296C4FB44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A013091C-203C-48BA-8CD2-317296C4FB44}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {86B02D30-0D88-436B-BEAE-84202E5FBFF8}
EndGlobalSection
EndGlobal

View File

@@ -1,81 +0,0 @@
## PX-TCP1 Demo Software (WIP)
Abstract
--------
ProtosXdigitalDemo is a simple C# console application that connects over TCP/IP to a `Protos-X PX-TCP1` Modbus master device. It reads digital inputs and analog registers, displays raw values and flattened bits, then exercises all digital outputs in an offonoff sequence. The program reports errors using predefined exit codes.
Requirements
------------
`.NET 8.0 SDK` or later
`NModbus4` NuGet package
• Access to a Protos-X PX-TCP1
How to Build
------------
1. Clone or download the repository to your machine.
2. Open a command prompt or terminal in the project folder.
3. Restore dependencies and compile:
```
dotnet restore
dotnet build
```
How to Test
-----------
1. Ensure the Protos-X PX-TCP1 is powered on and reachable at 10.10.1.1:502 (or change the IP/port constants in `Program.cs`).
2. In the project folder, run:
```
dotnet run -- [recipeName] [timesToRepeat]
```
• `recipeName` (optional): a label to pass in.
• `timesToRepeat` (optional): number of times to repeat the sequence (future extension).
3. Follow the on-screen prompts. The program will:
- Read and display 16 discrete inputs.
- Read and display 8 analog input registers.
- Convert those registers into 128 bits and display each bits On/Off state.
- Turn all 16 outputs OFF, wait for key press, then ON, wait again, then OFF.
Features / Control Flow
-----------------------
1. Parse up to two command-line arguments (recipe name and repeat count).
2. Load standardized failure codes.
3. Connect via TCP to the Protos-X PX-TCP1 master.
4. Read discrete inputs through `InputReader` and display them.
5. Read input registers through `RegisterReader`, display raw values, flatten to bits, and display.
6. Exercise outputs in sequence via `OutputExerciser`:
a. Set all outputs OFF → prompt user →
b. Set all outputs ON → prompt user →
c. Set all outputs OFF again
7. On any error (connection, read/write, argument count), print an error and exit with the designated code.
Exit Codes
----------
• 0 Success
• 1 Write-error to hardware
• 2 Too many command-line arguments
• 255 Other fatal error
## Warning
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
`TL;DR: Not my problem`
## License
The `GPL V2` license applies to this project. All copyrights belong to their respective copyright holders and all trademarks belong to their trademark holders.
```
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.)

View File

@@ -1,31 +0,0 @@
using System;
using System.Net.Sockets;
using static System.Console;
namespace ProtosXdigitalDemo
{
internal class TcpClientConnector
{
private readonly string _ip;
private readonly int _port;
public TcpClientConnector(string ip, int port)
{
_ip = ip;
_port = port;
}
public TcpClient Connect()
{
try
{
return new TcpClient(_ip, _port);
}
catch (Exception ex)
{
WriteLine($"[Error] Unable to connect to {_ip}:{_port} → {ex.Message}");
throw;
}
}
}
}

View File

@@ -1,77 +0,0 @@
using System.Runtime.InteropServices;
using static System.Console;
namespace ProtosXdigitalDemo
{
static class WindowUtility
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOZORDER = 0x0004;
private static Size GetScreenSize() => new Size(GetSystemMetrics(0), GetSystemMetrics(1));
private struct Size
{
public int Width { get; set; }
public int Height { get; set; }
public Size(int width, int height)
{
Width = width;
Height = height;
}
}
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GetConsoleWindow();
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
private static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(HandleRef hWnd, out Rect lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct Rect
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
private static Size GetWindowSize(IntPtr window)
{
if (!GetWindowRect(new HandleRef(null, window), out Rect rect))
throw new Exception("Unable to get window rect!");
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
return new Size(width, height);
}
public static void MoveWindowToCenter()
{
IntPtr window = GetConsoleWindow();
if (window == IntPtr.Zero)
throw new Exception("Couldn't find a window to center!");
Size screenSize = GetScreenSize();
Size windowSize = GetWindowSize(window);
int x = (screenSize.Width - windowSize.Width) / 2;
int y = (screenSize.Height - windowSize.Height) / 2;
SetWindowPos(window, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
public static void SetAppearanceOptions()
{
if (!IsOutputRedirected) // set console window size
{
BufferWidth = 150;
SetWindowSize(BufferWidth, 50);
}
}
}
}

View File

@@ -1,25 +0,0 @@
using System;
using static System.Console;
using Modbus.Device; // Hard dependency on Nmodbus4
using System.Net.Sockets;
namespace ProtosXdigitalDemo
{
internal class WriteMachineDigitalStateToIO
{
public static byte WriteMachineStateToIO(ModbusIpMaster master, ushort coilOutputStartAddress)
{
try
{
master.WriteMultipleCoils(coilOutputStartAddress, Data.MachineState.digitalOutputChannels);
}
catch (Exception ex)
{
WriteLine($"Error writing discrete outputs: {ex.Message}");
_ = ReadKey(true);
return Data.MachineState.failureCode[1];
}
return Data.MachineState.failureCode[0];
}
}
}