I'm trying to implement a simple TCP socket server in Swift. This code should be fairly similar to a C implementation but with extra Swift-isms.
I'm trying to connect using curl http://localhost:8080 which should at least trigger an "accept" even if there is no HTTP logic in the server. The response from curl is Connection refused
Here is the code I am using. It outputs 2 "Listening on descriptor" lines, but no "Accepting…" lines
import LibC
import Dispatch
let port = 8080
let listenBacklogLen = 16
// Setup Sockets
let socketDescriptor = (
ipv4: socket(PF_INET, SOCK_STREAM, IPPROTO_TCP),
ipv6: socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)
)
let descriptors = [socketDescriptor.ipv4, socketDescriptor.ipv6]
var address = (
ipv4: sockaddr_in(),
ipv6: sockaddr_in6()
)
address.ipv4.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
address.ipv4.sin_family = sa_family_t(AF_INET)
address.ipv4.sin_port = in_port_t(port)
address.ipv4.sin_addr.s_addr = INADDR_ANY
address.ipv6.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
address.ipv6.sin6_family = sa_family_t(AF_INET6)
address.ipv6.sin6_port = in_port_t(port)
address.ipv6.sin6_addr = in6addr_any
var reuseAddress: Int = 1
let reuseAddressLen = socklen_t(MemoryLayout.size(ofValue: reuseAddress))
withUnsafePointer(to: &reuseAddress) { ref in
for descriptor in descriptors {
assert(setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR, ref, reuseAddressLen) == 0)
}
}
withUnsafePointer(to: &address.ipv4) { ref in
ref.withMemoryRebound(to: sockaddr.self, capacity: 1) { reboundRef in
assert(bind(socketDescriptor.ipv4, reboundRef, socklen_t(MemoryLayout.size(ofValue: ref.pointee))) == 0)
}
}
withUnsafePointer(to: &address.ipv6) { ref in
ref.withMemoryRebound(to: sockaddr.self, capacity: 1) { reboundRef in
assert(bind(socketDescriptor.ipv6, reboundRef, socklen_t(MemoryLayout.size(ofValue: ref.pointee))) == 0)
}
}
var acceptSources: [DispatchSourceRead] = []
var requestSources: [DispatchSourceRead] = []
for descriptor in descriptors {
assert(listen(descriptor, Int32(listenBacklogLen)) == 0);
let source = DispatchSource.makeReadSource(fileDescriptor: descriptor, queue: .global(qos: .userInitiated))
source.setEventHandler {
print("Accepting…")
var address = sockaddr()
var addressLen: socklen_t = socklen_t(MemoryLayout.size(ofValue: address))
let requestSocketDescriptor = accept(descriptor, &address, &addressLen)
assert(requestSocketDescriptor >= 0)
let source = DispatchSource.makeReadSource(fileDescriptor: requestSocketDescriptor, queue: .global(qos: .userInitiated))
source.setEventHandler { [unowned source] in
var char: CChar = 0
let bytesRead = read(requestSocketDescriptor, &char, 1)
switch bytesRead {
case -1:
if ![EAGAIN, EINTR].contains(errno) {
print("Read returned error (\(errno)): \(strerror(errno))")
fallthrough
}
case 0:
print("Done")
source.cancel()
default:
print("C: \(char)")
}
}
requestSources.append(source)
source.resume()
}
acceptSources.append(source)
print("Listening on descriptor: \(descriptor)")
source.resume()
}
dispatchMain()
BTW, LibC is a simple module which unifies glibc and Darwin:
#if os(Linux)
#_exported import Glibc
#else
#_exported import Darwin.C
#endif
Aha! The port must be big-endian:
address.ipv4.sin_port = in_port_t(port).bigEndian
address.ipv6.sin_port = in_port_t(port).bigEndian
Figured this out with help from: Socket Server Example with Swift
Related
I want to import data from Excel files into SQL Server. The size of the file is 22 MB and contains approximately 1 million rows, but I get the error timeout.
This is the code of my controller
[System.Web.Http.Route("UploadExcel")]
[System.Web.Http.HttpPost]
[RequestFormLimits(MultipartBodyLengthLimit = 409715200)]
[RequestSizeLimit(409715200)]
public string ExcelUpload()
{
string message = "";
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
using (AngularDBEntities objEntity = new AngularDBEntities())
{
if (httpRequest.Files.Count > 0)
{
HttpPostedFile file = httpRequest.Files[0];
Stream stream = file.InputStream;
IExcelDataReader reader = null;
if (file.FileName.EndsWith(".xls"))
{
reader = ExcelReaderFactory.CreateBinaryReader(stream);
}
else if (file.FileName.EndsWith(".xlsx"))
{
reader = ExcelReaderFactory.CreateOpenXmlReader(stream);
}
else
{
message = "This file format is not supported";
}
DataSet excelRecords = reader.AsDataSet();
reader.Close();
var finalRecords = excelRecords.Tables[0];
for (int i = 0; i < finalRecords.Rows.Count; i++)
{
UserDetail objUser = new UserDetail();
objUser.UserName = finalRecords.Rows[i][0].ToString();
objUser.EmailId = finalRecords.Rows[i][1].ToString();
objUser.Gender = finalRecords.Rows[i][2].ToString();
objUser.Address = finalRecords.Rows[i][3].ToString();
objUser.MobileNo = finalRecords.Rows[i][4].ToString();
objUser.PinCode = finalRecords.Rows[i][5].ToString();
objEntity.UserDetails.Add(objUser);
}
int output = objEntity.SaveChanges();
if (output > 0)
{
message = "Excel file has been successfully uploaded";
}
else
{
message = "Excel file uploaded failed";
}
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
return message;
}
I added maxRequestLength="1048576" executionTimeout="999999" to the web.config file in the system.web section, and maxAllowedContentLength="1073741824" to security tag, but I am still facing this problem.
Knowing that when I upload small files, the data is added to the table
you can add all items in a list and finally use bulk insert. use can use Entity Framework Extensions.
I am trying to make a link between a Python script and a simulation running on UnetSim.
I want to send messages from Python and receive them in UnetStack using a Python_Agent.groovy that I created and added to container of each node in the simulation. I also want to do the reverse.
I used the fjage documentation (https://buildmedia.readthedocs.org/media/pdf/fjage/dev/fjage.pdf) to help me. The problem is that in gateway class Services there is not the PYTHON_AGENT Service that I created. I can understand that as my enum Services does not modified class Services where there are NODE_INFO, PHYSICAL etc...
My question is then how the example in the documentation 1.6.3 works ? And is it applicable to my case ?
Here is my code :
PythonSocketExample.py
from unetpy import *
from fjagepy import *
import socket
node_address = '001'
host = socket.gethostname()
sock = UnetSocket(host, int(node_address) + 1100)
gw = sock.getGateway()
py_agent = gw.agentForService(Services.PYTHON_AGENT)
py_agent << DatagramReq(data = '$A001')
rsp = py_agent.receive()
print (rsp)
UnetSimulation.groovy
//! Simulation : Initialisation python
import org.arl.fjage.RealTimePlatform
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.phy.*
import org.arl.unet.sim.*
import org.arl.unet.sim.channels.*
import static org.arl.unet.Services.*
import static org.arl.unet.phy.Physical.*
//import java.net.ServerSocket
///////////////////////////////////////////////////////////////////////////////
// simulation settings
platform = RealTimePlatform // use real-time mode
///////////////////////////////////////////////////////////////////////////////
// channel and modem settings
channel = [
model: ProtocolChannelModel,
soundSpeed: 1500.mps,
communicationRange: 3.km,
interferenceRange: 3.km
]
modem.model = USMARTModem
modem.dataRate = [640.bps, 6400.bps]
modem.frameLength = [16.bytes, 64.bytes]
modem.powerLevel = [0.dB, -10.dB]
modem.headerLength = 0
modem.preambleDuration = 0
modem.txDelay = 0
///////////////////////////////////////////////////////////////////////////////
// nodes settings and geometry
def beacons = 2..4 // 3 anchors from 2 to 4
def sensors = 5..104 // 100 sensors from 5 to 104
def nodeLocation = [:]
def D = 4000.m // side of the simulation area
def L = 400.m // distance between two node
nodeLocation[1] = [D/2-L, D/2 -L, -5.m] //masterAnchor
nodeLocation[2] = [D/2+L, D/2 -L, -5.m]
nodeLocation[3] = [D/2, D/2+L, -5.m]
nodeLocation[4] = [D/2, D/2, -500.m]
sensors.each { myAddr ->
nodeLocation[myAddr] = [rnd(0, D), rnd(0, D), rnd(-480.m, -500.m)]
}
///////////////////////////////////////////////////////////////////////////////
// simulation details
simulate {
node '1', address: 1, location: nodeLocation[1], web: 8101, api: 1101, shell: true, stack: {
container -> container.add 'py_agent' + 1, new Python_Agent()
}
beacons.each { myAddr ->
def myNode = node("${myAddr}", address: myAddr, location: nodeLocation[myAddr], web: 8100 + myAddr , api: 1100 + myAddr,
stack: {container ->
container.add 'py_agent' + myAddr, new Python_Agent()})
}
sensors.each { myAddr ->
def myNode = node("${myAddr}", address: myAddr, location: nodeLocation[myAddr], web: 8100 + myAddr, api: 1100 + myAddr,
stack: {container ->
container.add 'py_agent' + myAddr, new Python_Agent()})
}
Python_Agent.groovy
import org.arl.fjage.*
import org.arl.unet.*
enum Services {
PYTHON_AGENT
}
class Python_Agent extends UnetAgent {
String fromNode;
String toNode;
String toClient;
def nodeInfo;
def myLocation;
def myAddress;
def phy;
void setup() {
register Services.PYTHON_AGENT
}
void startup() {
// TODO
nodeInfo = agentForService(Services.NODE_INFO)
myLocation = nodeInfo.location
myAddress = nodeInfo.address
println('pyAgent ' + myAddress + ' works')
}
void processMessage(Message msg) {
if (msg instanceof DatagramNtf /*&& msg.protocol == NODE_STATUS_PROTOCOL*/) {
println("Node "+ myAddress+ ' receiving ' + msg.text +' from ' + msg.from +" protocol is "+ msg.protocol)
toNode = phy.energy
}
}
}
The first error that i get is :
1 error
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
C:\Users\mathi\OneDrive\Bureau\unet-3.0.0\FakeModem\python.groovy: 85: Enum constructor calls are only allowed inside the enum class
. At [85:50] # line 85, column 50.
container.add 'py_agent' + 1, new Python
^
then if I comment the enum part and modifie the setup part, the simulation works
void setup() {
register 'PYTHON_AGENT'
}
When I run PythonSocketExample.py, I get the error
Traceback (most recent call last):
File "PythonSocketExample.py", line 11, in <module>
py_agent = gw.agentForService(Services.PYTHON_AGENT)
AttributeError: type object 'Services' has no attribute 'PYTHON_AGENT'
The end of the log on UnetStack is here:
1582820223131 INFO Python_Agent/84#1903:println pyAgent 84 works
1582820223132 INFO Python_Agent/39#1633:println pyAgent 39 works
1582820415798 INFO org.arl.unet.sim.SimulationMasterContainer#48:connected Incoming connection tcp:///137.195.214.230:1101//137.195.214.230.62913
1582820415875 INFO org.arl.unet.sim.SimulationMasterContainer#2131:connectionClosed Connection tcp:///137.195.214.230:1101//137.195.214.230.62913 closed
Thank you for your help
EDIT
Thanks to your message and some research I am now able to send and receive message between UnetStack and Python by using MessageBehavior and GenericMessage.
I want my simulation to receive more than one message but as my add new MessageBehavior is in startup() of my PythonAgent.groovy I need as much add new MessageBehavior as message that I send.
I tested to put it on a processMessage(Message msg) but it seems this method does not recognize GenericMessage().
The question could be how to use MessageBehavior more than one time...
Here is my code :
Python
ping_test.py
# Need to run the script two times. First time UnetStack don't get the Message
# I don't know where the bug come from
serport = serial.Serial()
## SET SELF ADDRESS
nodeID = '001'
nodeID_ping = '002'
command = b'$A' + nodeID.encode()
serport.write(command)
ack_msg = serport.readline()
print('ack_msg : ', ack_msg)
ping_command = b'$P' + nodeID_ping.encode()
serport.write(ping_command)
ack_msg = serport.readline()
print('ack_msg :', ack_msg)
rsp_msg = serport.readline()
print('rsp_msg :', rsp_msg)
FakeSerial_V2.py
from unetpy import *
import socket
import clientSocket
# a Serial class emulator
class Serial:
## init(): the constructor. Many of the arguments have default values
# and can be skipped when calling the constructor.
def __init__( self, port='5000', baudrate = 19200, timeout=1, write_timeout=1,
bytesize = 8, parity = 'N', stopbits = 1, xonxoff=0,
rtscts = 0):
self.last_instruction = ''
self.nodeID = ''
self.remote_nodeID = ''
self.command = ''
self._isOpen = True
self._receivedData = ''
self._data = 'It was the best of times.\nIt was the worst of times.\n'
self.phy = ''
self.pySocket = ''
## write()
# writes a string of characters to the Arduino
def write( self, string):
self.command = string.decode()
_type = None
print( 'FakeSerial got: ' + self.command)
# SET_ADDRESS
if (self.command[0:2] == '$A' and len(self.command) == 5):
_type = 'set_address'
self.nodeID = string[2:]
self.pySocket = clientSocket.clientSocket(self.nodeID) # initialize the clientSocket class
self.pySocket.sendData(_type) # need to fix the rsp Generic Message on UnetStack
self.last_instruction = 'SET_ADDRESS_INSTRUCTION'
# PING
elif (self.command[0:2] == '$P' and len(self.command) == 5):
_type = 'ping'
to_addr = self.command[2:]
# print(to_addr, type(to_addr))
self.pySocket.sendData(_type, to_addr)
self.last_instruction = "PING_INSTRUCTION"
else:
print("write FAILURE")
## readline()
# reads characters from the fake Arduino until a \n is found.
def readline( self ):
self._receivedData = self.pySocket.receiveData()
return self._receivedData
clientSocket.py
import socket
from unetpy import *
from fjagepy import *
class clientSocket:
def __init__(self, nodeID = '001'):
self.host = socket.gethostname()
self.nodeID = int(nodeID)
self.port = int(nodeID) + 1100
self.sock = UnetSocket(self.host, self.port)
self.gw = self.sock.getGateway()
self.pyagent = 'pyagent' + str(self.nodeID)
def sendData(self, _type, to_addr = '000' , data = 'None'):
IDreq = 1
# gmsg = GenericMessage(perf = Performative.REQUEST, recipient = pyagent)
# gmsg.IDreq = IDreq
# self.gw.send(gmsg)
IDreq = IDreq + 1
gmsg2 = GenericMessage(perf = Performative.REQUEST, recipient = self.pyagent)
gmsg2.type = _type
gmsg2.from_addr = self.nodeID
gmsg2.to_addr = int(to_addr)
gmsg2.data = data
gmsg2.IDreq = IDreq
self.gw.send(gmsg2)
IDreq = 0
def receiveData( self ):
rgmsg = self.gw.receive(GenericMessage, 4000)
print ('UnetStack state :', rgmsg.state)
# print ('rsp :', rgmsg.data)
# print('Ping time is', rgmsg.time_ping, 'ms')
return rgmsg.data
Groovy
sim1.groovy
import org.arl.fjage.RealTimePlatform
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.sim.channels.*
platform = RealTimePlatform // use real-time mode
///////////////////////////////////////////////////////////////////////////////
// channel and modem settings
channel = [
model: ProtocolChannelModel,
soundSpeed: 1500.mps,
communicationRange: 3.km,
interferenceRange: 3.km
]
modem.model = USMARTModem
modem.dataRate = [640.bps, 6400.bps]
modem.frameLength = [16.bytes, 64.bytes]
modem.powerLevel = [0.dB, -10.dB]
modem.headerLength = 0
modem.preambleDuration = 0
modem.txDelay = 0
simulate {
node '1', address: 1, web: 8101, api: 1101, stack: {
container -> container.add 'pyagent1', new PythonAgent()
}
node '2', address: 2,location: [500.m ,500.m, -500.m], web: 8102, api: 1102, stack: {
container -> container.add 'pyagent2', new PythonAgent()
}
}
PythonAgent.groovy
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.phy.RxFrameNtf
import org.arl.unet.phy.TxFrameNtf
class PythonAgent extends UnetAgent {
final static int PING_PROTOCOL = 10;
final static int NODE_STATUS_PROTOCOL = 11;
final static int BROADCAST_PROTOCOL = 12;
final static int UNICAST_PROTOCOL = 13;
final static int UNICAST_ACK_PROTOCOL = 14;
final static int TEST_MSG_PROTOCOL = 15;
final static int ECHO_PROTOCOL = 16;
final static int QUALITY_PROTOCOL = 17;
def nodeInfo;
def phy;
def myLocation;
def myAddress;
def IDreq = 0;
def time_ping = null;
def function_state = null;
def data_to_py = null;
void startup() {
println(agentID.name + ' running')
nodeInfo = agentForService Services.NODE_INFO
phy = agentForService Services.PHYSICAL
myLocation = nodeInfo.location
myAddress = nodeInfo.address
subscribe topic(phy)
add new MessageBehavior(GenericMessage, { req ->
println("In PythonAgent::MessageBehavior req ="+req)
if (req.performative) println("req.performative is " + req.performative)
else println("req.performative is null")
def ack = new GenericMessage(req, Performative.INFORM)
def rsp = new GenericMessage(req, Performative.INFORM)
println('IDreq = ' + req.IDreq)
if ((req.performative == Performative.REQUEST) && (req.IDreq == 2)) {
// IDreq = req.IDreq
// println('IDreq = ' + IDreq)
//log.info "Generic message request of type ${req.type}"
function_state = 'None';
data_to_py = 'None';
switch (req.type) {
case 'set_address':
println("Handling set_address")
ack.state = "Handling set_address"
ack.data = '#A' + corrected_address(myAddress);
send ack;
rsp.data = ack.data; break;
}
}
})
add new MessageBehavior(GenericMessage, { req ->
println("In PythonAgent::MessageBehavior req ="+req)
if (req.performative) println("req.performative is " + req.performative)
else println("req.performative is null")
def ack = new GenericMessage(req, Performative.INFORM)
def rsp = new GenericMessage(req, Performative.INFORM)
println('IDreq = ' + req.IDreq)
if ((req.performative == Performative.REQUEST) && (req.IDreq == 2)) {
// IDreq = req.IDreq
// println('IDreq = ' + IDreq)
//log.info "Generic message request of type ${req.type}"
function_state = 'None';
data_to_py = 'None';
switch (req.type) {
case 'set_address':
println("Handling set_address")
ack.state = "Handling set_address"
ack.data = '#A' + corrected_address(myAddress);
send ack;
rsp.data = ack.data; break;
case 'loc':
//println("Handling localisation request");
sendUPSBeacon(); break;
case 'ping':
println("Handling ping request");
ack.state = "Handling ping request"; ack.data = '$P' + corrected_address(req.to_addr);
send ack;
ping(req.to_addr);
rsp.time_ping = time_ping; break;
case 'exe':
//println("Handling exe request");
exe(); break;
case 'sense':
//println("Handling sense request");
sense(); break;
default: println "Unknown request";
}
//println "In USMARTBaseAnchorDaemon::MessageBehavior, just after exe"
rsp.state = function_state
rsp.data = data_to_py
println "In PythonAgent::MessageBehavior, rsp is " + rsp
send rsp
}
})
}
void ping(to_addr) {
println "Pinging ${to_addr} at ${nanoTime()}"
DatagramReq req = new DatagramReq(to: to_addr, protocol: PING_PROTOCOL)
phy << req
def txNtf = receive(TxFrameNtf, 10000) // TO-DO:check protocol
def rxNtf = receive({ it instanceof RxFrameNtf && it.from == req.to}, 10000)
if (txNtf && rxNtf && rxNtf.from == req.to) {
time_ping = (rxNtf.rxTime-txNtf.txTime)/1000 //in ms
println("Response from ${rxNtf.from}: ")
println("rxTime=${rxNtf.rxTime}")
println("txTime=${txNtf.txTime}")
println("Response from ${rxNtf.from}: time = ${time_ping}ms")
function_state = 'Ping processed'
data_to_py = "#R" + corrected_address(to_addr) + 'T' + rxNtf.data
}
else {
function_state = 'Ping Request timeout'
println (function_state)
}
}
#Override
void processMessage(Message msg) {
// pong
if (msg instanceof DatagramNtf && msg.protocol == PING_PROTOCOL) {
println("pong : Node "+ myAddress + ' from ' + msg.from +" protocol is "+ msg.protocol)
send new DatagramReq(recipient: msg.sender, to: msg.from, data: phy.energy as byte[], protocol: PING_PROTOCOL)
println ('processMessage energy : ' + phy.energy)
}
}
String corrected_address(address) {
address = address.toString()
if (address.size() == 1) address = '00' + address
if (address.size() == 2) address = '0' + address
return address
}
}
USMARTModem.groovy
import org.arl.fjage.Message
import org.arl.unet.sim.HalfDuplexModem
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.phy.*
import org.arl.unet.sim.*
import org.arl.unet.sim.channels.*
import static org.arl.unet.Services.*
import static org.arl.unet.phy.Physical.*
/*
Ptx= V*Itx //power consumed in transmission in watt
Prx = V*Irx //power consumed in receiving packets in watt
Etx = Math.floor(avgSent)*(Ptx*0.3675)
energyAll = (Math.floor(avgSent)*(Ptx*0.3675)) + (Math.floor(avgReceived)*(Prx*0.3675)) // total energy consumed for all the packets sent and received throughout the simulation
// EtxSubset = Math.floor(avgTxCountNs)*(Ptx*0.3675) // energy consumed in transmitiing 25% of packets in Joul
bytesDelivered = Math.floor(avgReceived)* modem.frameLength[1]
JPerBit = energyAll/(bytesDelivered * 8)
*/
//Duration of data packet in seconds = data packet size (in bits)/bandwidth (in bps) = (15*8)/50000 = 0.0024
class USMARTModem extends HalfDuplexModem {
static final def txPower = -17.dB
static final def acousticDataRate = 640.bps
static final def payLoadSize = 5.bytes
static final def headerDuration = (30+75+200)/1000 //in seconds --> in our case nanomodem v3 provides us with the header (in ms) to add to the actual payload in the frame length.. refer to the modem datasheet
static final def V = 5 // supply voltage in volt
static final def Itx = 0.3, Irx = 0.005, Iidle = 0.0025 //current in Am
float payLoadDuration = (payLoadSize*8)/acousticDataRate //in seconds
float dataPacketDuration = payLoadDuration +headerDuration //in seconds
float energy = 2000 //initial energy in Joule
float test = energy+1
float Ptx = V*Itx, Prx=V*Irx, Pidle = V*Iidle //power in watt
float totalEtx =0
float totalErx =0
float totalEidle =0
float totalEnergyConsumed =0
float Etx = Ptx * dataPacketDuration //Energy in Joul
float Erx = Prx * dataPacketDuration
float Eidle = Pidle * dataPacketDuration
// float power = 0.00001995262315 //in watt (-17 in db=10log(p/1mw) .. so p = 10to the power -1.7 = 0.00001995262315
// BigDecimal Ptx = (Math.pow(10.0,(txPower/10) ))/1000 //????
// BigDecimal Etx= Ptx *((frameLength[1]*8)/640) // This is consumed energy (in transmission) Etx = Ptx*time it takes to tramsnit tha packet
//float Etx =10
#Override
boolean send(Message m) {
if (m instanceof TxFrameNtf)
{
energy -= Etx// Remaining energy
totalEtx += Etx //total energy consumed in tx
}
if (m instanceof RxFrameNtf)
{
energy -= Erx // Remaining energy
totalErx += Erx //total energy consumed in rx
}
if(!busy)
{
energy-= Eidle //Remaining energy
totalEidle += Eidle //total energy consumed while Eidle
}
totalEnergyConsumed = totalEtx+totalErx+totalEidle
return super.send(m)
}
}
Sorry for the very very long post...I think everything was necessary to understand the code
A few problems in your original code:
You don't need to create a service, since you can address the agent by its name. This should be sufficient for your example here.
To process a request (DatagramReq from your Python code), you should override the processRequest() method in the agent.
Here's a simplified example based on your original code:
PythonAgent.groovy:
import org.arl.fjage.*
import org.arl.unet.*
class PythonAgent extends UnetAgent {
void startup() {
println('pyAgent running')
}
#Override
Message processRequest(Message msg) {
if (msg instanceof DatagramReq) {
println('Got a DatagramNtf')
// do whatever you want with the request
return new Message(msg, Performative.AGREE)
}
return null
}
}
sim1.groovy:
import org.arl.fjage.RealTimePlatform
platform = RealTimePlatform // use real-time mode
simulate {
node '1', address: 1, web: 8101, api: 1101, stack: {
container -> container.add 'pyagent', new PythonAgent()
}
}
and test1.py:
from unetpy import *
from fjagepy import *
sock = UnetSocket('localhost', 1101) # node 1's API port as per sim script
gw = sock.getGateway()
pyagent = gw.agent('pyagent') # agent name as per sim script
rsp = pyagent << DatagramReq()
print(rsp)
Thank you, I did not knew I needed to #Override. I still have a question, how can I put data in my DatagramReq that I can extract in UnetStack ?
I tried this as a first solution looking at the Handbook but it doesn't works..
PythonAgent.groovy
import org.arl.fjage.*
import org.arl.unet.*
class PythonAgent extends UnetAgent {
void startup() {
println('pyAgent running')
}
#Override
Message processRequest(Message msg) {
if (msg instanceof DatagramReq) {
println('Got a DatagramNtf')
println(msg.data)
// do whatever you want with the request
return new Message(msg, Performative.AGREE)
}
return null
}
}
test1.py
from unetpy import *
from fjagepy import *
sock = UnetSocket('localhost', 1101) # node 1's API port as per sim script
gw = sock.getGateway()
pyagent = gw.agent('pyagent') # agent name as per sim script
rsp1 = pyagent << DatagramReq( data = [42])
rsp2 = pyagent << DatagramReq( data = 'data_string')
print(rsp1, rsp2)
On the Python terminal I will get Agree None. I can transmit an array but not a string ?
The log print
Incoming connection tcp:///127.0.0.1:1101//127.0.0.1.51208
1583166206032 INFO PythonAgent/1#2643:println Got a DatagramNtf
1583166206032 INFO PythonAgent/1#2643:println [B#4e3d88df
1583166206033 WARNING org.arl.fjage.remote.ConnectionHandler#2670:run Bad JSON request: java.lang.IllegalArgumentException: Illegal base64 character 5f in {"action": "send", "relay": true, "message": { "clazz": "org.arl.unet.DatagramReq", "data": {"msgID":"492ac9dd-c2bf-4c0c-9198-3b32fb416f33","perf":"REQUEST","recipient":"pyagent","sender":"PythonGW-c8e66e0f-b5d5-433b-bfa9-09be708ab4c9","inReplyTo":null,"data":"data_string"} }}
1583166207081 INFO org.arl.unet.sim.SimulationMasterContainer#2670:connectionClosed Connection tcp:///127.0.0.1:1101//127.0.0.1.51208 closed
[B#4e3d88df correspond to [42] but I don't know how to decode it. And in fact I am more interested in sending string than array. I have a track about using PDU but how could it works with Python ?
My problem is:
Livolo switches have their own Zigbee gate. I want to connect them from zigbee2mqtt with CC2531 USB dongle. In general it works, but when I turn on/off switch button (on physical device), the switch sends an incorrect ZCL package.
I'm an absolutely newbie in microcontrollers programming and in Zigbee architecture. So I hope someone can help me and answer these questions:
Where I can intercept that malformed package?
How I can fix that package to support the Zigbee standard?
I use Z-STACK-HOME 1.2.2a firmware and compile it as described there:
https://github.com/Koenkk/Z-Stack-firmware/blob/master/coordinator/Z-Stack_Home_1.2/COMPILE.md
// Malformed ZCL package
// header
0x7c, // [0111 1100]
// 01 - frame type = "Command is specific or local to a cluster"
// 1 - manufacturer spec = manufacturer code present
// 1 - direction = "from server to client"
// 1 - disable default response
// 100 - reserved
0xd2, 0x15, // manufacturer
0xd8, // sequence
0x00, // read attrs command
// endpoint adress
0x12, 0x0f, 0x05, 0x18, 0x00, 0x4b, 0x12, 0x00,
0x22, 0x00, // ????? need more data from another switches
0x03 // 0x00|0x01|0x02|0x03 - switch state
upd:
I think, what I can intercept message in AF.c file in afIncomingData function and fix in afBuildMSGIncoming.
So now I hope, someone can help me with the right message format. Which can be processed standard ZCL parcer.
void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId,
NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse,
uint32 timestamp, uint8 radius )
{
endPointDesc_t *epDesc = NULL;
epList_t *pList = epList;
#if !defined ( APS_NO_GROUPS )
uint8 grpEp = APS_GROUPS_EP_NOT_FOUND;
#endif
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the first endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND ) {
// No endpoint found, default to endpoint 1.
// In the original source code there is a return here.
// This prevent the messags from being forwarded.
// For our use-case we want to capture all messages.
// Even if the coordinator is not in the group.
epDesc = afFindEndPointDesc( 1 );
}
else {
epDesc = afFindEndPointDesc( grpEp );
}
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return; // Not supported
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
// Set the list
if ( pList != NULL )
{
epDesc = pList->epDesc;
}
}
else if ( aff->DstEndPoint == 10 || aff->DstEndPoint == 11 ) {
if ( (epDesc = afFindEndPointDesc( 1 )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
}
else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
while ( epDesc )
{
uint16 epProfileID = 0xFFFE; // Invalid Profile ID
if ( pList->pfnDescCB )
{
uint16 *pID = (uint16 *)(pList->pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint ));
if ( pID )
{
epProfileID = *pID;
osal_mem_free( pID );
}
}
else if ( epDesc->simpleDesc )
{
epProfileID = epDesc->simpleDesc->AppProfId;
}
// First part of verification is to make sure that:
// the local Endpoint ProfileID matches the received ProfileID OR
// the message is specifically send to ZDO (this excludes the broadcast endpoint) OR
// if the Wildcard ProfileID is received the message should not be sent to ZDO endpoint
if ( (aff->ProfileID == epProfileID) ||
((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) ||
((epDesc->endPoint != ZDO_EP) && ( aff->ProfileID == ZDO_WILDCARD_PROFILE_ID )) )
{
// Save original endpoint
uint8 endpoint = aff->DstEndPoint;
// overwrite with descriptor's endpoint
aff->DstEndPoint = epDesc->endPoint;
afBuildMSGIncoming( aff, epDesc, SrcAddress, SrcPanId, sig,
nwkSeqNum, SecurityUse, timestamp, radius );
// Restore with original endpoint
aff->DstEndPoint = endpoint;
}
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the next endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return;
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
pList = pList->nextDesc;
if ( pList )
epDesc = pList->epDesc;
else
epDesc = NULL;
}
else
epDesc = NULL;
}
}
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig,
uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp, uint8 radius )
{
afIncomingMSGPacket_t *MSGpkt;
const uint8 len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength;
uint8 *asdu = aff->asdu;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len );
if ( MSGpkt == NULL )
{
return;
}
MSGpkt->hdr.event = AF_INCOMING_MSG_CMD;
MSGpkt->groupId = aff->GroupID;
MSGpkt->clusterId = aff->ClusterID;
afCopyAddress( &MSGpkt->srcAddr, SrcAddress );
MSGpkt->srcAddr.endPoint = aff->SrcEndPoint;
MSGpkt->endPoint = epDesc->endPoint;
MSGpkt->wasBroadcast = aff->wasBroadcast;
MSGpkt->LinkQuality = sig->LinkQuality;
MSGpkt->correlation = sig->correlation;
MSGpkt->rssi = sig->rssi;
MSGpkt->SecurityUse = SecurityUse;
MSGpkt->timestamp = timestamp;
MSGpkt->nwkSeqNum = nwkSeqNum;
MSGpkt->macSrcAddr = aff->macSrcAddr;
MSGpkt->macDestAddr = aff->macDestAddr;
MSGpkt->srcAddr.panId = SrcPanId;
MSGpkt->cmd.TransSeqNumber = 0;
MSGpkt->cmd.DataLength = aff->asduLength;
MSGpkt->radius = radius;
if ( MSGpkt->cmd.DataLength )
{
MSGpkt->cmd.Data = (uint8 *)(MSGpkt + 1);
osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength );
}
else
{
MSGpkt->cmd.Data = NULL;
}
#if defined ( MT_AF_CB_FUNC )
// If ZDO or SAPI have registered for this endpoint, dont intercept it here
if (AFCB_CHECK(CB_ID_AF_DATA_IND, *(epDesc->task_id)))
{
MT_AfIncomingMsg( (void *)MSGpkt );
// Release the memory.
osal_msg_deallocate( (void *)MSGpkt );
}
else
#endif
{
// Send message through task message.
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
}
}
I don't think you're decoding the frame control byte correctly. Looking at some code I've written, I interpret it as follows:
0x7c, // [0111 1100]
// 011 - reserved
// 1 - disable default response
// 1 - direction = "from server to client"
// 1 - manufacturer spec = manufacturer code present
// 00 - frame type = "Command acts across entire profile"
This is based on an old ZCL spec (around 2008?) and perhaps the reserved bits have taken on some meaning in a later version of the spec.
I believe the manufacturer specific bit indicates that this is not a standard Zigbee command (Read Attributes). If it was Read Attributes, I think it should have an even number of bytes following it (16-bit attribute IDs).
What were the source and destination endpoints, profile ID and cluster ID for that frame?
Update:
It looks like you could modify afIncomingData() to look at the fields of aff to identify this exact message type (frame control, endpoints, cluster, profile), and then hand it off to your own function for processing (and responding if necessary).
But I'd also take a look at documentation for MT_AF_CB_FUNC and MT_AfIncomingMsg() to see if that's the "official" way to identify frames that you want to handle in your own code.
In AF.c file in functions afIncomingData and afBuildMSGIncoming added bloks marked by
#if defined ( LIVOLO_SUPPORT )
#endif
In afIncomingData I add:
#if defined ( LIVOLO_SUPPORT )
else if ( aff->DstEndPoint == 0x08 )
{
if ( (epDesc = afFindEndPointDesc( 1 )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
}
#endif
It's prewent filtering message sended to unknown destEndpoint
And in afBuildMSGIncoming function:
#if defined ( LIVOLO_SUPPORT )
uint8 fixedPackage[] = {
0x18, 0xd8, 0x01, // header
0x00, 0x00, // attrId
0x00, // success
0x10, // boolean
0x00
};
if (aff->SrcEndPoint == 0x06 && aff->DstEndPoint == 0x01
&& aff->ClusterID == 0x0001 && aff->ProfileID == 0x0104) {
const uint8 mlfrmdHdr[] = { 0x7c, 0xd2, 0x15, 0xd8, 0x00 };
if (osal_memcmp(asdu, mlfrmdHdr, 5) == TRUE) {
fixedPackage[7] = asdu[aff->asduLength - 1];
MSGpkt->cmd.DataLength = 8; // sizeof(fixedPackage)
MSGpkt->clusterId = 0x06; // genOnOff
asdu = fixedPackage;
}
}
#endif
It change unsupported package to readAttrResp package.
I have a client that retrieves a certificate (.pfx), including a private key, from a server and I add this to the local keychain with the following code: -
void AddCertToKeyChain(const QByteArray& cert, const QString& password)
{
SecKeychainRef keyChain = nil;
OSStatus err = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &keyChain);
if (err != errSecSuccess)
{
emit Log("Failed to access system keychain: " + LogMessageForStatus(err));
return;
}
SecExternalFormat format = kSecFormatPKCS12;
SecExternalItemType itemType = kSecItemTypeAggregate;
SecItemImportExportFlags flags = 0;
SecItemImportExportKeyParameters params;
memset(¶ms, 0, sizeof(params));
params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
params.flags = 0;
params.passphrase = password.toCFString();
params.alertTitle = NULL;
params.alertPrompt = NULL;
params.accessRef = NULL;
// create and populate the key usage array
CFMutableArrayRef keyUsage = CFArrayCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks
);
CFArrayAppendValue(keyUsage, kSecAttrCanEncrypt);
CFArrayAppendValue(keyUsage, kSecAttrCanDecrypt);
CFArrayAppendValue(keyUsage, kSecAttrCanDerive);
CFArrayAppendValue(keyUsage, kSecAttrCanSign);
CFArrayAppendValue(keyUsage, kSecAttrCanVerify);
CFArrayAppendValue(keyUsage, kSecAttrCanWrap);
CFArrayAppendValue(keyUsage, kSecAttrCanUnwrap);
keyUsage = NULL; // Error without this - Failed to import certificate: The key usage mask is not supported.
// create and populate the key attributes array
CFMutableArrayRef keyAttributes = CFArrayCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks
);
// required for import
params.keyUsage = keyUsage;
params.keyAttributes = keyAttributes;
OSStatus status = SecItemImport(cert.toCFData(), CFSTR(".p12"), &format, &itemType, flags, ¶ms, keyChain, NULL);
if(status == errSecSuccess)
emit Log("Certificate successfully imported");
else
{
emit Log("Failed to import certificate: " + LogMessageForStatus(status));
}
}
The certificate and private key appear in the keychain, as expected.
However, trying to retrieve the certificate is a problem, either programmatically or using the Keychain application.
If I select to export the private key from the keychain, I'm provided with the following error in a dialog: -
"An error has occurred. Unable to export an item. The contents of this item cannot be retrieved"
However, if the certificate and key are added to the keychain by double-clicking on the pfx, exporting the key works as expected.
So, why would the code above cause the problem of not being able to export the key?
With the assistance of Quinn at Apple, It seems that the method described in the question should work, but doesn't.
Using an old CDSA style flag instead does in fact work, doing something like this: -
OSStatus err;
SecExternalFormat format;
SecItemImportExportKeyParameters params;
params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
params.flags = 0;
params.passphrase = (__bridge CFStringRef) pkcs12Password;
params.alertTitle = NULL;
params.alertPrompt = NULL;
params.accessRef = NULL;
params.keyUsage = NULL;
params.keyAttributes = (__bridge CFArrayRef) #[ #(CSSM_KEYATTR_EXTRACTABLE) ];
format = kSecFormatPKCS12;
err = SecItemImport(
(__bridge CFDataRef) pkcs12Data,
CFSTR("p12"),
&format,
NULL,
0,
¶ms,
keychain,
NULL
);
Note the setting of params.keyAttributes, which defines the key to be extractable.
Alternatively, the older (deprecated) SecKeychainItemImport API may be used: -
BOOL success;
OSStatus err;
NSArray * result;
SecExternalFormat format;
SecKeyImportExportParameters params;
CFArrayRef importedItems;
result = nil;
importedItems = NULL;
format = kSecFormatPKCS12;
memset(¶ms, 0, sizeof(params));
params.passphrase = password;
params.keyAttributes = CSSM_KEYATTR_EXTRACTABLE;
err = SecKeychainItemImport(
(CFDataRef) pkcs12Blob, // importedData
NULL, // fileNameOrExtension
&format, // inputFormat
NULL, // itemType
0, // flags
¶ms, // keyParams
self->keychain, // importKeychain
&importedItems // outItems
);
success = (err == noErr);
While the function SecKeychainItemImport is defined as deprecated in Apple's documentation, I have been informed that it's unlikely to be removed any time soon.
I am trying to copy a text file from one folder to another. Issue is once you select the folder to save to what is the proper code to get the file to copy to that folder? I and using NSI Filepicker modeOpen and modeSave and can't find any code on how to save file properly. the MDN is lacking code.
var dispdir = Components.classes["#mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("ProfD", Components.interfaces.nsIFile);
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["#mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, "Select a File", nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterText);
fp.displayDirectory = dispdir;
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnCancel) {
var file = fp.file;
var path = fp.file.path;
}
var savedir = Components.classes["#mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("ProfD", Components.interfaces.nsIFile);
savedir.append("Test Folder");
if( !savedir.exists() || !savedir.isDirectory() ) {
// if it doesn't exist,create
savedir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
alert(savedir.path + "\n" + "Folder was made");
}
var fp2 = Components.classes["#mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp2.init(window, "Save file to?", nsIFilePicker.modeSave);
fp2.appendFilters(nsIFilePicker.filterText);
fp2.displayDirectory = savedir;
fp2.defaultString = fp.file.leafName;
var rv = fp2.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
}
var aDir = Components.classes["#mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
aDir.append(fp2.file.parent.path);
alert(fp2.file.parent.path)
fp.file.copyTo(aDir, null);
copyFile(fp.file.path);
alert(fp2.file.path + "\n" + "File copied successfuly!")
Use fp.file to create a stream, see
https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Synchronous
I have a question for you though, where is 'window' from fp.init(window defined?