OpenProtocolInterpreter 6.1.1 — PowerMACS 4000 Bugs
Environment
- Library: OpenProtocolInterpreter 6.1.1
- Controller: PowerMACS 4000 (Station: OP2160)
- Open Protocol: Revision 1 handshake (MID 0001/0002)
- Connection: TCP, length-prefix framing
Summary
Five bugs found while building a production gateway service that connects to real PowerMACS 4000 controllers:
- Mid0105.Pack() — length prefix larger than actual body, controller hangs
- Mid0106.Pack() — length prefix mismatch + field offset corruption, cannot roundtrip
- Mid0107.Pack() — NPE when BoltResults is non-empty
- Mid0002.Parse() — fails on PowerMACS 4000 minimal format, fields parsed as 0/null
- Mid0008 / Mid0009.Pack() — length prefix too large, controller hangs
Bug 1: Mid0105.Pack() — length prefix larger than actual body
var subscribe = new Mid0105(3) { SendOnlyNewData = true };
var packed = subscribe.Pack();
// packed starts with "0236" (declaring 236 bytes)
// but packed.Length == 226 bytes
Impact: The controller reads the declared length prefix and blocks waiting for 10 bytes that never arrive. Connection stalls indefinitely.
Workaround: Bypass PackBytes() entirely; send raw string padded to declared length:
if (msg.Length >= 4 && int.TryParse(msg.AsSpan(0, 4), out var declared) && msg.Length < declared)
msg = msg.PadRight(declared);
Bug 2: Mid0106.Pack() — length prefix mismatch + field offset corruption
var mid = new Mid0106
{
DataNumberSystem = 1001,
StationName = "ST-1",
ModeName = "Program-A",
WpId = "ABC-260617110000",
BoltsData = [new BoltData { OrdinalBoltNumber = 1 }],
};
var packed = mid.Pack();
// Length prefix: "0236", but packed.Length == 226
// Roundtrip failure:
var parsed = interpreter.Parse(packed) as Mid0106;
// parsed.DataNumberSystem == 10010405 ← expected 1001
// parsed.NumberOfBolts == 50 ← expected 1
Impact: The library cannot round-trip its own output. Tests that need to construct valid Mid0106 ASCII frames are impossible.
Bug 3: Mid0107.Pack() — NPE when BoltResults is populated
var mid = new Mid0107
{
BoltNumber = 1,
BoltName = "B-1",
ProgramName = "Program-A",
BoltResults = new List<BoltResult>
{
new() { VariableName = "TORQUE", Value = 12.5m, Type = "F" },
},
};
var packed = mid.Pack(); // NullReferenceException
Impact: Cannot construct Mid0107 messages containing bolt result variables.
Bug 4: Mid0002.Parse() — fails on PowerMACS 4000 minimal format
PowerMACS 4000 sends a minimal 57-byte Mid0002. Real frame captured from controller:
00570002001 01 02 03OP2160
Field breakdown:
| Offset |
Length |
Content |
Field |
| 0 |
4 |
0057 |
Length = 57 |
| 4 |
4 |
0002 |
MID = 0002 |
| 8 |
3 |
001 |
Revision = 1 |
| 11 |
1 |
|
NoAck flag |
| 12 |
9 |
|
Reserved |
| 21 |
2 |
01 |
Station ID |
| 23 |
4 |
|
Reserved |
| 27 |
2 |
02 |
Cell ID |
| 29 |
3 |
|
Reserved |
| 32 |
2 |
03 |
Channel ID |
| 34 |
23 |
OP2160 + 19 spaces |
Controller Name |
The frame ends after ControllerName. There is no SupplierCode, OpenProtocolVersion, ControllerSoftwareVersion, ToolSoftwareVersion, SystemType, etc.
Parse results vs actual values:
| Property |
Actual in frame |
Library Parse result |
| CellId |
02 |
0 |
| ChannelId |
03 |
0 |
| ControllerName |
OP2160 |
OP2160 (correct) |
| OpenProtocolVersion |
not present |
null |
| ControllerSoftwareVersion |
not present |
null |
| ToolSoftwareVersion |
not present |
null |
| SystemType |
not present |
SystemTypeNotSet |
| SystemSubType |
not present |
NoSubtypeExists |
Even the fields that ARE present (CellId=02, ChannelId=03) are parsed as 0.
Impact: Cannot determine controller firmware version, protocol revision, or system capabilities from the handshake response.
Bug 5: Mid0008 / Mid0009.Pack() — length prefix too large
Same pattern as Mid0105 — the length prefix exceeds actual output for messages with ExtraData fields.
var sub = new Mid0008
{
SubscriptionMid = "0900",
WantedRevision = 1,
ExtraDataLength = extraData.Length,
ExtraData = extraData,
};
var packed = sub.Pack();
// packed.Substring(0, 4) declares length X, but packed.Length < X
Workaround:
private static string NormalizeLength(string msg)
{
if (msg.Length >= 4 && int.TryParse(msg.AsSpan(0, 4), out var declared) && msg.Length < declared)
return msg.PadRight(declared);
return msg;
}
CurveSubscriptionMessage = NormalizeLength(BuildCurveSubscription(opt));
CurveUnsubscribeMessage = NormalizeLength(new Mid0009 { ... }.Pack());
Summary table
| MID |
Method |
Issue |
| Mid0105 |
Pack() |
Length prefix too large (0236 vs 226); controller hangs |
| Mid0106 |
Pack() |
Length prefix mismatch + field offset corruption; cannot roundtrip |
| Mid0107 |
Pack() |
NPE when BoltResults non-empty |
| Mid0002 |
Parse() |
Doesn't handle PowerMACS 4000 minimal format; fields parsed as 0/null |
| Mid0008 |
Pack() |
Length prefix too large; controller hangs |
| Mid0009 |
Pack() |
Length prefix too large; controller hangs |
Note: Parse() for Mid0106/Mid0107 works correctly on frames emitted by real PowerMACS controllers — the Pack bugs are one-directional. But Mid0002.Parse() is broken in both directions for PowerMACS 4000.
I don’t know if it’s because the controller firmware version is too low. Please answer it when you have time. I think this is a very good project and has helped me solve a lot of problems!
OpenProtocolInterpreter 6.1.1 — PowerMACS 4000 Bugs
Environment
Summary
Five bugs found while building a production gateway service that connects to real PowerMACS 4000 controllers:
Bug 1: Mid0105.Pack() — length prefix larger than actual body
Impact: The controller reads the declared length prefix and blocks waiting for 10 bytes that never arrive. Connection stalls indefinitely.
Workaround: Bypass
PackBytes()entirely; send raw string padded to declared length:Bug 2: Mid0106.Pack() — length prefix mismatch + field offset corruption
Impact: The library cannot round-trip its own output. Tests that need to construct valid Mid0106 ASCII frames are impossible.
Bug 3: Mid0107.Pack() — NPE when BoltResults is populated
Impact: Cannot construct Mid0107 messages containing bolt result variables.
Bug 4: Mid0002.Parse() — fails on PowerMACS 4000 minimal format
PowerMACS 4000 sends a minimal 57-byte Mid0002. Real frame captured from controller:
Field breakdown:
00570002001010203OP2160+ 19 spacesThe frame ends after
ControllerName. There is noSupplierCode,OpenProtocolVersion,ControllerSoftwareVersion,ToolSoftwareVersion,SystemType, etc.Parse results vs actual values:
OP2160OP2160(correct)SystemTypeNotSetNoSubtypeExistsEven the fields that ARE present (
CellId=02,ChannelId=03) are parsed as 0.Impact: Cannot determine controller firmware version, protocol revision, or system capabilities from the handshake response.
Bug 5: Mid0008 / Mid0009.Pack() — length prefix too large
Same pattern as Mid0105 — the length prefix exceeds actual output for messages with
ExtraDatafields.Workaround:
Summary table
Note:
Parse()for Mid0106/Mid0107 works correctly on frames emitted by real PowerMACS controllers — the Pack bugs are one-directional. But Mid0002.Parse() is broken in both directions for PowerMACS 4000.I don’t know if it’s because the controller firmware version is too low. Please answer it when you have time. I think this is a very good project and has helped me solve a lot of problems!