/// <summary> /// Convert raw message to BabelMessage. /// Called by a port with an incoming raw message. /// Must return as soon as possible, so convert and put on incoming Q for later relay. /// Caller frees the underlying raw message. /// TODO: Process long message if necessary: start a long list. /// </summary> /// <param name="exchange"></param> /// <param name="mtx"></param> /// <returns></returns> public static byte ProcessIncomingRawMesssage(MessageExchange exchange, MessageTransaction mtx) { PacketBuffer g = mtx.MsgBuffer; BabelMessage m = new BabelMessage(exchange); m.Pid = (byte)(g.flagsPid & ProtocolConstants.PID_MASK); m.Verified = (m.Pid == ProtocolConstants.PID_GENERAL_V); m.Receiver = ProtocolConstants.ADRS_LOCAL; m.FlagsRS = g.flagsRS(); m.IncomingNetIfIndex = mtx.ONetIfIndex; m.OutgoingNetIfIndex = ProtocolConstants.NETIF_UNSET; m.Cmd = 0; switch (m.Pid) { case ProtocolConstants.PID_GENERAL: case ProtocolConstants.PID_GENERAL_V: m.IsGeneral = true; m.IsHandshake = false; m.Receiver = g.receiver(); m.Cmd = g.command(); m.FlagsRS = g.flagsRS(); break; default: return(1); } m.Sender = g.sender(); m.SenderId = g.senderId(); m.DataLen = g.dataLength(); m.DataAry = mtx.CopyMessageData(); // Now submit message to be relayed later. exchange.SubmitIncomingMessage(m); return(0); }
// Helper routine for finalizing message state ready for sending. // If justFree, simply frees message buffer. // If dispatchAction=BabelConstants.SEND_TO_ROUTER, sends message to router. // If dispatchAction=BabelConstants.POST_TO_WRITE, pushes message onto oPort's Write Q. public void FinishMessageTransaction() { if (Finish == FinishAction.Free) { Manager.IoBuffersFreeHeap.Release(IoIndex); } else if (Finish == FinishAction.Keep) { } else { if (ReturnCmd != 0) { MsgBuffer.command(ReturnCmd); } if (ChangeDir) { // Change general message direction: // r<>s, RNetIf<>SNetIf, rPort<>sPort, // Invert Is Reply flag, clear ACK flag byte ports, flags = MsgBuffer.flagsRS(); ushort sender = MsgBuffer.sender(); MsgBuffer.sender(MsgBuffer.receiver()); MsgBuffer.receiver(sender); flags ^= ProtocolConstants.MESSAGE_FLAGS_IS_REPLY; flags &= Primitives.ByteNeg(ProtocolConstants.MESSAGE_FLAGS_ACK); // Now swap ports. ports = (byte)(flags & ProtocolConstants.MESSAGE_PORTS_MASK); ports = (byte)((ports << 2) | (ports >> 2)); MsgBuffer.flagsRS((byte)((ports & ProtocolConstants.MESSAGE_PORTS_MASK) | (flags & ProtocolConstants.MESSAGE_FLAGS_MASK))); } else { // Caller might be relaying message to another adrs:netIf. // So leave flagsRS alone. } MsgBuffer.pktLen = (byte)(ProtocolConstants.GENERAL_OVERHEADS_SIZE + MsgBuffer.dataLength()); MsgBuffer.UpdateCheckSum(MsgBuffer.pktLen); if (Dispatch == Router.RouterAction.SendToRouter) { Router.RouteMessage(Manager, IoIndex); } else if (Dispatch == Router.RouterAction.PostToNetIf) { Router.PostMessage(Manager, IoIndex, ONetIfIndex); } } }
// Create a command general message. public int CreateGeneralMessage(bool verified, byte cmd, ushort receiver, ushort sender, byte flagsRS, byte senderId, byte dataLen, byte[] pData, byte bufferIndex) { if (dataLen > (Manager.MaxPacketSize - ProtocolConstants.GENERAL_OVERHEADS_SIZE)) { return(-1); } int idx = Manager.IoBuffersFreeHeap.Allocate(); if (idx != -1) { PacketBuffer b = Manager.IoBuffers[idx]; byte pid = verified ? ProtocolConstants.PID_GENERAL_V : ProtocolConstants.PID_GENERAL; b.flagsPid = pid; b.dNetIf = ProtocolConstants.NETIF_UNSET; b.iNetIf = ProtocolConstants.NETIF_UNSET; b.pktLen = (byte)(ProtocolConstants.GENERAL_OVERHEADS_SIZE + dataLen); b.sync(ProtocolConstants.BYTE_SYNC); b.negPidPid(Primitives.NibbleToPid(pid)); b.destination(LinkMonitor.Destination); // TODO: set destination. b.arg(0); b.receiver(receiver); b.sender(sender); b.senderId(senderId); b.flagsRS(flagsRS); b.command(cmd); b.dataLength(dataLen); byte dataIndex = ProtocolConstants.GENERAL_DATA_ARRAY_OFFSET; byte[] p = b.buffer; for (byte k = 0; k < dataLen; k++) { p[dataIndex++] = pData[bufferIndex + k]; } b.UpdateCheckSum(b.pktLen); } return(idx); }
// Process Port commands. // Returns zero on success. public override byte CommandHandler(MessageTransaction mtx) { PacketBuffer g = mtx.MsgBuffer; Boolean isReply = ((g.flagsRS() & ProtocolConstants.MESSAGE_FLAGS_IS_REPLY) != 0); if (((!isReply) && (g.command() < ProtocolConstants.MEDIATOR_CONTROL_CMD_BASE)) || (isReply && (g.senderId() == ProtocolConstants.IDENT_MEDIATOR))) { byte dataIndex = ProtocolConstants.GENERAL_DATA_ARRAY_OFFSET; byte[] tmp; switch (g.command()) { // Standard device commands. case ProtocolConstants.MEDIATOR_DEVICE_RESET: // Reset the module. Manager.AppBabelDeviceReset(); break; case ProtocolConstants.MEDIATOR_DEVICE_STATUS: // Get device status. if (isReply) { return(1); } g.dataLength(Diagnostics.GetDeviceStatus(Manager, g.buffer)); mtx.ChangeDir = true; mtx.Finish = MessageTransaction.FinishAction.Normal; break; case ProtocolConstants.MEDIATOR_DEVICE_TICKER: // Get device ticker count. if (isReply) { return(1); // We've been sent a ticker count. } // Sender wants to know ticker count. tmp = Primitives.UIntToByteArray(Primitives.GetBabelMilliTicker()); return(mtx.StoreMessageValue(tmp, (byte)(tmp.Length))); case ProtocolConstants.MEDIATOR_DEVICE_ERASE: // TODO: erase eeprom and restart. return(2); case ProtocolConstants.MEDIATOR_DEVICE_READVAR: return((MediatorReadCommand(mtx) == 0) ? (byte)1 : (byte)0); case ProtocolConstants.MEDIATOR_DEVICE_WRITEVAR: return((MediatorWriteCommand(mtx) == 0) ? (byte)1 : (byte)0); case ProtocolConstants.MEDIATOR_DEVICE_ISOVAR: return((MediatorIsoCommand(mtx) == 0) ? (byte)1 : (byte)0); case ProtocolConstants.MEDIATOR_DEVICE_ISOMONVAR: return((MediatorIsoCommand(mtx) == 0) ? (byte)1 : (byte)0); case ProtocolConstants.MEDIATOR_DEVICE_ISOMSG: return((MediatorIsoCommand(mtx) == 0) ? (byte)1 : (byte)0); case ProtocolConstants.MEDIATOR_DEVICE_LOG: if (!isReply) { if (Settings.LogOn) { uint ticks = (uint)Primitives.GetArrayValueU(g.buffer, dataIndex, 4); string s; byte logType = g.buffer[dataIndex + 4]; if (Settings.LogBinary || logType == 'B') { int start = dataIndex + 5; int last = dataIndex + g.dataLength(); s = "("; for (int k = start; k < last; k++) { s = s + g.buffer[k].ToString("X2") + " "; } s += ")"; } else { s = Primitives.GetArrayStringValue(g.buffer, dataIndex + 5, g.dataLength() - 5); } Log.d("MediatorNetIf:" + Exchange.Id, ":" + ticks + ":" + s); } } return(1); // Connection specific commands. case ProtocolConstants.MEDIATOR_DEVICE_GETSN: // Get SN of receiver device at end of link. if (isReply) // We've been sent an SN for SPort. { g.buffer[dataIndex + g.dataLength()] = 0; Manager.SerialNumberManager.UpdateSerialNumbers(g.buffer, dataIndex, g.dataLength(), g.iNetIf, g.sender()); if (mtx.MsgBuffer.iNetIf != ProtocolConstants.NETIF_USER_BASE) // Notify master of SN via connection attach command. { SendLinkMediatorCommand(this, true, ProtocolConstants.MEDIATOR_CONNECT_ATTACH, g.dataLength(), g.buffer, dataIndex); } break; } // Sender wants to know SN of master. g.dataLength(Manager.SerialNumberManager.CopyMasterSerialNumber(g.buffer, dataIndex, g.iNetIf)); mtx.ChangeDir = true; mtx.Finish = MessageTransaction.FinishAction.Normal; break; case ProtocolConstants.MEDIATOR_CONNECT_ATTACH: // TODO: Report attach and SN of device. case ProtocolConstants.MEDIATOR_CONNECT_DETACH: // TODO: Report detach of device. case ProtocolConstants.MEDIATOR_CONNECT_GATEWAY: // TODO: Register as a gateway. return(10); default: // TODO: try handling via NETIF_MEDIATOR_PORT. return(11); } return(0); } return(BabelMessage.ProcessIncomingRawMesssage(Exchange, mtx)); }
// Setup an isochronous task. // Format: // for ISO[MON]VAR: as per standard ReadCommand, // but CmdFlags preceded by: ISODetails={IsoId,IsoIntLo,IsoIntHi,RepeatLo,RepeatHi}, // for ISOMSG: <cmd=ISOMSG> ISODetails {msgCmd,...}, // where: IsoInt is the millisecond interval between reads, 0=remove, // Repeat is the number of times to send data, 0=forever. // Id for deleting task is SenderParams. int MediatorIsoCommand(MessageTransaction mtx) { PacketBuffer g = mtx.MsgBuffer; IsoTask t; byte[] pAry; int dataIndex; int pIndex; uint msInterval, repeatCount; byte n, k, isoId; bool isIsoMsg; n = g.dataLength(); if (n < 3) { return(0); } pAry = g.buffer; pIndex = dataIndex = ProtocolConstants.GENERAL_DATA_ARRAY_OFFSET; isoId = pAry[dataIndex++]; msInterval = pAry[dataIndex++]; msInterval += (uint)(pAry[dataIndex++] << 8); // First check if this is a cancel or update request. // Find isoTask, if it exists, using isoId + senderParams as id. for (k = 0; k < NumTasks; k++) { do { byte j; t = IsoTasks[IsoTaskQueue[k]]; if (t.isoId == isoId && t.addressParams.Sender == g.sender() && t.addressParams.SenderId == g.senderId() && t.addressParams.flagsRS == (g.flagsRS() & ProtocolConstants.MESSAGE_PORTS_MASK)) { // Free message buffer. Manager.IoBuffersFreeHeap.Release(t.ioIndex); t.ioIndex = -1; // Remove from task queue. for (j = k; j < (NumTasks - 1); j++) { IsoTaskQueue[j] = IsoTaskQueue[j + 1]; } --NumTasks; if (k < NumTasks) { continue; } } break; } while (true); } if (msInterval == 0) { return(0); } isIsoMsg = (g.command() == ProtocolConstants.MEDIATOR_DEVICE_ISOMSG); if (isIsoMsg) { if (n < 6) { return(0); // Must be at least 5 iso parameters + 1 cmd byte. } } else if (n < 8) { return(0); // Must be at least 5 iso parameter bytes + 3 read bytes. } // Set up a new isoTask. // First check if a task slot is free. if (NumTasks == IsoTaskSize) { return(0); } t = null; for (k = 0; k < IsoTaskSize; k++) { if (IsoTasks[k].ioIndex == -1) { t = IsoTasks[k]; break; } } if (t == null) { return(0); } // Second, get iso params. repeatCount = pAry[dataIndex++]; repeatCount += (uint)(pAry[dataIndex++] << 8); t.isoId = isoId; t.repeatCount = repeatCount; t.msTicksRelativeToGo = 0; // Schedule for immediate execution because inserted at head of Q. t.msInterval = msInterval; t.ioIndex = mtx.IoIndex; t.state = 0; t.cmd = g.command(); // Original msg iso cmd. t.ownerDriver = (isIsoMsg ? Manager.GetLinkDriver(ProtocolConstants.NETIF_MEDIATOR_PORT) : this); // For IsoMsg, needs to be MediatorNetIf. t.addressParams.SenderId = g.senderId(); t.addressParams.Sender = g.sender(); t.addressParams.flagsRS = (byte)(g.flagsRS() & ProtocolConstants.MESSAGE_PORTS_MASK); // Now convert iso to a regular read message. // For ISOMSG cmd, g->command needs to be pulled from next dataAry byte. if (isIsoMsg) { g.command(pAry[dataIndex++]); --n; } else { g.command(ProtocolConstants.MEDIATOR_DEVICE_READVAR); } n -= 5; g.dataLength(n); while (n-- > 0) { pAry[pIndex++] = pAry[dataIndex++]; } mtx.Finish = MessageTransaction.FinishAction.Keep; // Keep iobuffer. // Finally insert task at head of queue. for (n = (byte)NumTasks; n > 0; n--) { IsoTaskQueue[n] = IsoTaskQueue[n - 1]; } IsoTaskQueue[0] = k; ++NumTasks; return(1); }
// Write parameter values to RAM or EEPROM. // Writes default value if no value given. // Command syntax: data[]={writeId, cmdFlags, page, <numWritten:>numToWrite, {parameterIndex}+[data]. // Returns number written along with parameter Index's. byte MediatorWriteCommand(MessageTransaction mtx) { DeviceParameterTable pt = Exchange.ParameterTable; DeviceParameter d; PacketBuffer g = mtx.MsgBuffer; byte[] pAry; int dataIndex; VariableValue val = null; byte n, h, k, j, writeId, cmdFlags, pageNum, retArgsLen, maxSize = 0; VariableKind parameterVarKind; StorageFlags parameterFlags; byte parameterSize, numToWrite; int paramIndexes, numWrittenIndex; n = g.dataLength(); if (n < 5) { return(0); } dataIndex = ProtocolConstants.GENERAL_DATA_ARRAY_OFFSET; pAry = g.buffer; writeId = pAry[dataIndex++]; cmdFlags = pAry[dataIndex++]; pageNum = pAry[dataIndex++]; numWrittenIndex = dataIndex; numToWrite = (byte)(pAry[dataIndex++] & 0xf); n -= 4; if (numToWrite > n) { numToWrite = n; } j = numToWrite; if (numToWrite > 8) { numToWrite = 8; } paramIndexes = dataIndex; retArgsLen = (byte)(dataIndex + numToWrite); g.dataLength((byte)(g.dataLength() - (4 + j))); //3=(wId, cmdFlags, page, nW:nTW), j= num original idx's. dataIndex += j; h = 0; k = 0; while (k < numToWrite) { int paramIndex = pAry[paramIndexes + k]; if (paramIndex > pt.GetLastEntryIndex()) { break; } d = pt.Find(paramIndex); if (d == null) { d = DeviceParameterTable.NullParameter; } parameterSize = (byte)d.ParamSize; if (parameterSize != 0) { int valPtrIndex = 0; parameterVarKind = (VariableKind)d.ParamVarKind; parameterFlags = (StorageFlags)d.ParamStorageFlags; if (parameterVarKind == VariableKind.VK_String) // Note: only one string allowed per message. { parameterSize = 0; if (n > 0) // Strings must be at least 1 byte long: len byte. { byte lenAdj; maxSize = (byte)d.MaxValue.ValueLong; parameterSize = pAry[dataIndex++]; lenAdj = parameterSize; n--; if (parameterSize > maxSize) { parameterSize = maxSize; } if (n < parameterSize) { parameterSize = n; lenAdj = parameterSize; } valPtrIndex = dataIndex; n -= lenAdj; dataIndex += lenAdj; } } else { if (n >= parameterSize) { val = new VariableValue(parameterVarKind, pAry, dataIndex, (int)parameterSize); valPtrIndex = dataIndex; n -= parameterSize; dataIndex += parameterSize; } else { val = d.DefaultValue; valPtrIndex = -1; } val.CheckInRange(d.MinValue, d.MaxValue); } if (parameterSize > 0) { if ((parameterFlags & StorageFlags.SF_ReadOnly) == 0) { if (((cmdFlags & ProtocolConstants.MEDIATOR_DEVICE_RWV_FLAGS_EEPROM) != 0) && ((parameterFlags & StorageFlags.SF_EEPROM) != 0)) { if (parameterVarKind == VariableKind.VK_String) { if (valPtrIndex >= 0) { d.EEPromValue = new VariableValue(parameterVarKind, pAry, valPtrIndex, (int)parameterSize); } else { d.EEPromValue = new VariableValue(parameterVarKind, pAry, 0, (int)0); } } else { d.EEPromValue = val; } } if ((cmdFlags & ProtocolConstants.MEDIATOR_DEVICE_RWV_FLAGS_RAM) != 0) { if (parameterVarKind == VariableKind.VK_String) { if (valPtrIndex >= 0) { d.RamValue = new VariableValue(parameterVarKind, pAry, valPtrIndex, (int)parameterSize); } else { d.RamValue = new VariableValue(parameterVarKind, pAry, 0, (int)0); } } else { d.RamValue = val; } } } } ++h; } ++k; ++paramIndex; } if (numWrittenIndex >= 0) { pAry[numWrittenIndex] = h; // Reply to caller with number of parameters written. } g.dataLength(retArgsLen); mtx.ChangeDir = true; mtx.Finish = ((g.flagsRS() & ProtocolConstants.MESSAGE_FLAGS_ACK) != 0) ? MessageTransaction.FinishAction.Normal : MessageTransaction.FinishAction.Free; return(h); }