Changeset 330:ae65c735022a
- Timestamp:
- 11/11/12 14:44:58 (12 years ago)
- Branch:
- default
- Phase:
- public
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/mlx/fsuipc.py
r321 r330 146 146 self._extra = extra 147 147 self._validator = validator 148 148 149 149 def process(self, time): 150 150 """Process the request. … … 152 152 Return True if the request has succeeded, False if data validation 153 153 has failed for a reading request. An exception may also be thrown 154 if there is some lower-level communication problem.""" 154 if there is some lower-level communication problem.""" 155 155 if self._forWrite: 156 156 pyuipc.write(self._data) … … 180 180 self._extra = extra 181 181 self._validator = validator 182 182 183 183 @property 184 184 def id(self): … … 203 203 if time<self._nextFire: 204 204 return True 205 205 206 206 if self._preparedData is None: 207 207 self._preparedData = pyuipc.prepare_data(self._data) … … 214 214 while self._nextFire <= time: 215 215 self._nextFire += self._period 216 216 217 217 return isOK 218 218 … … 237 237 self._requestCondition = threading.Condition() 238 238 self._connectionRequested = False 239 self._connected = False 239 self._connected = False 240 240 241 241 self._requests = [] … … 321 321 self._connectionRequested = True 322 322 self._requestCondition.notify() 323 323 324 324 def disconnect(self): 325 325 """Disconnect from the flight simulator.""" … … 327 327 self._requests = [] 328 328 if self._connectionRequested: 329 self._connectionRequested = False 329 self._connectionRequested = False 330 330 self._requestCondition.notify() 331 331 … … 339 339 while True: 340 340 self._waitConnectionRequest() 341 341 342 342 if self._connect()>0: 343 343 self._handleConnection() 344 344 345 345 self._disconnect() 346 346 347 347 def _waitConnectionRequest(self): 348 348 """Wait for a connection request to arrive.""" … … 350 350 while not self._connectionRequested: 351 351 self._requestCondition.wait() 352 352 353 353 def _connect(self, autoReconnection = False, attempts = 0): 354 354 """Try to connect to the flight simulator via FSUIPC … … 361 361 self._connectionRequested = False 362 362 if autoReconnection: 363 Handler._callSafe(lambda: 363 Handler._callSafe(lambda: 364 364 self._connectionListener.disconnected()) 365 365 else: 366 Handler._callSafe(lambda: 366 Handler._callSafe(lambda: 367 367 self._connectionListener.connectionFailed()) 368 368 return 0 369 369 370 370 try: 371 371 attempts += 1 372 372 pyuipc.open(pyuipc.SIM_ANY) 373 373 description = "(FSUIPC version: 0x%04x, library version: 0x%04x, FS version: %d)" % \ 374 (pyuipc.fsuipc_version, pyuipc.lib_version, 374 (pyuipc.fsuipc_version, pyuipc.lib_version, 375 375 pyuipc.fs_version) 376 376 if not autoReconnection: … … 378 378 if pyuipc.fs_version == pyuipc.SIM_FSX \ 379 379 else const.SIM_MSFS9 380 381 Handler._callSafe(lambda: 380 381 Handler._callSafe(lambda: 382 382 self._connectionListener.connected(fsType, 383 383 description)) … … 389 389 if attempts<self.NUM_CONNECTATTEMPTS: 390 390 time.sleep(self.CONNECT_INTERVAL) 391 391 392 392 def _handleConnection(self): 393 393 """Handle a living connection.""" … … 412 412 (timeout is not None and timeout <= 0.0): 413 413 return 414 414 415 415 self._requestCondition.wait(timeout) 416 416 417 417 def _disconnect(self): 418 418 """Disconnect from the flight simulator.""" … … 421 421 pyuipc.close() 422 422 self._connected = False 423 423 424 424 def _processRequest(self, request, time, attempts): 425 """Process the given request. 425 """Process the given request. 426 426 427 427 If an exception occurs or invalid data is read too many times, we try … … 457 457 finally: 458 458 self._requestCondition.acquire() 459 459 460 460 def _processRequests(self): 461 461 """Process any pending requests. … … 471 471 if request.nextFire>t: 472 472 break 473 473 474 474 attempts = self._processRequest(request, t, attempts) 475 475 … … 493 493 (0x023c, "b"), # UTC minute 494 494 (0x023a, "b") ] # seconds 495 495 496 496 normalData = timeData + \ 497 497 [ (0x3d00, -256), # The name of the current aircraft … … 502 502 (0x31e4, "d"), # Radio altitude 503 503 (0x02c8, "d") ] # Vertical speed 504 504 505 505 flareStartData = [ (0x0e90, "H"), # Ambient wind speed 506 506 (0x0e92, "H"), # Ambient wind direction 507 507 (0x0e8a, "H") ] # Visibility 508 508 509 509 flareData2 = [ (0x023a, "b"), # Seconds of time 510 510 (0x0366, "H"), # On the ground … … 526 526 timestamp += data[2] * 3600 527 527 timestamp += data[3] * 60 528 timestamp += data[4] 528 timestamp += data[4] 529 529 530 530 return timestamp … … 542 542 543 543 data.append((offset + 2, "b", 0)) 544 545 data.append((offset + 3, "b", 0)) 544 545 data.append((offset + 3, "b", 0)) 546 546 547 547 def __init__(self, connectionListener, connectAttempts = -1, 548 548 connectInterval = 0.2): 549 549 """Construct the simulator. 550 550 551 551 The aircraft object passed must provide the following members: 552 552 - type: one of the AIRCRAFT_XXX constants from const.py … … 559 559 visibility is in metres. flareStart and flareStartFS are two time 560 560 values expressed in seconds that can be used to calculate the flare 561 time. 561 time. 562 562 - flareFinished(flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS, 563 563 ias, pitch, bank, heading): called when the flare has … … 580 580 self._syncTime = False 581 581 self._nextSyncTime = -1 582 582 583 583 self._normalRequestID = None 584 584 … … 630 630 631 631 These values will be passed to the callback function in this order, as 632 separate arguments.""" 632 separate arguments.""" 633 633 self._handler.requestRead([(0x13fc, "d")], self._handlePayloadCount, 634 634 extra = callback) … … 638 638 self._handler.requestRead(Simulator.timeData, self._handleTime, 639 639 extra = callback) 640 640 641 641 def startMonitoring(self): 642 642 """Start the periodic monitoring of the aircraft and pass the resulting 643 643 state to the aircraft object periodically.""" 644 assert not self._monitoringRequested 644 assert not self._monitoringRequested 645 645 self._monitoringRequested = True 646 646 647 647 def stopMonitoring(self): 648 648 """Stop the periodic monitoring of the aircraft.""" 649 assert self._monitoringRequested 649 assert self._monitoringRequested 650 650 self._monitoringRequested = False 651 651 … … 721 721 self._nextSyncTime = -1 722 722 self._syncTime = True 723 723 724 724 def disableTimeSync(self): 725 725 """Enable the time synchronization.""" … … 740 740 self._hotkeySetGeneration = 0 741 741 self._hotkeyCallback = callback 742 742 743 743 self._handler.requestRead([(0x320c, "u")], 744 744 self._handleNumHotkeys, … … 765 765 self._hotkeyCallback = None 766 766 self._clearHotkeyRequest() 767 767 768 768 def disconnect(self, closingMessage = None, duration = 3): 769 769 """Disconnect from the simulator.""" 770 assert not self._monitoringRequested 770 assert not self._monitoringRequested 771 771 772 772 print "fsuipc.Simulator.disconnect", closingMessage, duration 773 773 774 774 self._stopNormal() 775 775 self.clearHotkeys() … … 787 787 if self._hotkeys is not None: 788 788 self._hotkeySetGeneration += 1 789 789 790 790 self._handler.requestRead([(0x320c, "u")], 791 791 self._handleNumHotkeys, … … 850 850 elif self._monitoring and self._aircraftModel is not None and \ 851 851 not createdNewModel: 852 aircraftState = self._aircraftModel.getAircraftState(self._aircraft, 852 aircraftState = self._aircraftModel.getAircraftState(self._aircraft, 853 853 timestamp, data) 854 854 855 855 self._checkTimeSync(aircraftState) 856 856 857 857 self._aircraft.handleState(aircraftState) 858 858 … … 870 870 if self._nextSyncTime > (now - 0.49): 871 871 return 872 872 873 873 self._handler.requestWrite([(0x023a, "b", int(seconds))], 874 874 self._handleTimeSynced) 875 875 876 876 #print "Set the seconds to ", seconds 877 877 878 878 if self._nextSyncTime<0: 879 879 self._nextSyncTime = now 880 880 881 881 self._nextSyncTime += Simulator.TIME_SYNC_INTERVAL 882 882 else: … … 886 886 """Callback for the time sync result.""" 887 887 pass 888 888 889 889 def _setAircraftName(self, timestamp, name, airPath): 890 890 """Set the name of the aicraft and if it is different from the 891 891 previous, create a new model for it. 892 892 893 893 If so, also notifty the aircraft about the change. 894 894 … … 912 912 if needNew: 913 913 self._setAircraftModel(AircraftModel.create(self._aircraft, aircraftName)) 914 915 914 915 916 916 self._aircraft.modelChanged(timestamp, self._latin1decoder(name)[0], 917 self._aircraftModel.name) 917 self._aircraftModel.name) 918 918 919 919 return needNew … … 925 925 will be replaced by a new one.""" 926 926 self._aircraftModel = model 927 927 928 928 if self._monitoring: 929 929 self._stopNormal() 930 930 self._startMonitoring() 931 931 932 932 def _startMonitoring(self): 933 933 """Start monitoring with the current aircraft model.""" 934 934 data = Simulator.normalData[:] 935 935 self._aircraftModel.addMonitoringData(data, self._fsType) 936 936 937 937 self._normalRequestID = \ 938 self._handler.requestPeriodicRead(1.0, data, 938 self._handler.requestPeriodicRead(1.0, data, 939 939 self._handleNormal, 940 940 validator = self._validateNormal) … … 955 955 self._handler.clearPeriodic(self._flareRequestID) 956 956 self._flareRequestID = \ 957 self._handler.requestPeriodicRead(0.1, 957 self._handler.requestPeriodicRead(0.1, 958 958 Simulator.flareData2, 959 959 self._handleFlare2) 960 960 self._handler.requestRead(Simulator.flareStartData, 961 961 self._handleFlareStart) 962 962 963 963 self._addFlareRate(data[2]) 964 964 … … 1005 1005 zfw = data[0] * const.LBSTOKG / 256.0 1006 1006 callback(zfw) 1007 1007 1008 1008 def _handleTime(self, data, callback): 1009 1009 """Callback for a time retrieval request.""" … … 1016 1016 for i in range(0, payloadCount): 1017 1017 data.append((0x1400 + i*48, "f")) 1018 1018 1019 1019 self._handler.requestRead(data, self._handleWeights, 1020 1020 extra = callback) … … 1040 1040 numHotkeys = data[0] 1041 1041 print "fsuipc.Simulator._handleNumHotkeys: numHotkeys:", numHotkeys 1042 data = [(0x3210 + i*4, "d") for i in range(0, numHotkeys)] 1042 data = [(0x3210 + i*4, "d") for i in range(0, numHotkeys)] 1043 1043 self._handler.requestRead(data, self._handleHotkeyTable, 1044 1044 (id, generation)) … … 1093 1093 self._handleHotkeysWritten, 1094 1094 (id, generation)) 1095 1095 1096 1096 def _handleHotkeysWritten(self, success, (id, generation)): 1097 1097 """Handle the result of the hotkeys having been written.""" 1098 with self._hotkeyLock: 1098 with self._hotkeyLock: 1099 1099 if success and id==self._hotkeySetID and \ 1100 1100 generation==self._hotkeySetGeneration: 1101 1101 data = [(offset + 3, "b") for offset in self._hotkeyOffets] 1102 1102 1103 1103 self._hotkeyRequestID = \ 1104 1104 self._handler.requestPeriodicRead(0.5, data, … … 1107 1107 1108 1108 def _handleHotkeys(self, data, (id, generation)): 1109 """Handle the hotkeys.""" 1109 """Handle the hotkeys.""" 1110 1110 with self._hotkeyLock: 1111 1111 if id!=self._hotkeySetID or generation!=self._hotkeySetGeneration: … … 1129 1129 1130 1130 def _handleHotkeysCleared(self, sucess, extra): 1131 """Callback for the hotkey-clearing write request.""" 1131 """Callback for the hotkey-clearing write request.""" 1132 1132 1133 1133 def _clearHotkeyRequest(self): … … 1136 1136 self._handler.clearPeriodic(self._hotkeyRequestID) 1137 1137 self._hotkeyRequestID = None 1138 1138 1139 1139 #------------------------------------------------------------------------------ 1140 1140 … … 1241 1241 return (extBCD[1] if extBCD[1]!="0" else "") + \ 1242 1242 mainBCD[1:] + "." + extBCD[3] 1243 1243 1244 1244 def __init__(self, flapsNotches): 1245 1245 """Construct the aircraft model. 1246 1246 1247 1247 flapsNotches is a list of degrees of flaps that are available on the aircraft.""" 1248 1248 self._flapsNotches = flapsNotches 1249 1249 1250 1250 @property 1251 1251 def name(self): 1252 1252 """Get the name for this aircraft model.""" 1253 1253 return "FSUIPC/Generic" 1254 1254 1255 1255 def doesHandle(self, aircraft, aircraftName): 1256 1256 """Determine if the model handles the given aircraft name. 1257 1257 1258 1258 This default implementation returns False.""" 1259 1259 return False … … 1261 1261 def _addOffsetWithIndexMember(self, dest, offset, type, attrName = None): 1262 1262 """Add the given FSUIPC offset and type to the given array and a member 1263 attribute with the given name.""" 1263 attribute with the given name.""" 1264 1264 dest.append((offset, type)) 1265 1265 if attrName is not None: … … 1275 1275 - the FSUIPC offset 1276 1276 - the FSUIPC type 1277 1277 1278 1278 The latter two items will be appended to dest.""" 1279 1279 for (name, offset, type) in data: 1280 1280 self._addOffsetWithIndexMember(dest, offset, type, prefix + name) 1281 1281 1282 1282 def addMonitoringData(self, data, fsType): 1283 1283 """Add the model-specific monitoring data to the given array.""" 1284 1284 self._addDataWithIndexMembers(data, "_monidx_", 1285 1285 AircraftModel.monitoringData) 1286 1286 1287 1287 def getAircraftState(self, aircraft, timestamp, data): 1288 1288 """Get an aircraft state object for the given monitoring data.""" 1289 1289 state = fs.AircraftState() 1290 1290 1291 1291 state.timestamp = timestamp 1292 1292 … … 1297 1297 360.0 / 65536.0 / 65536.0 / 65536.0 / 65536.0 1298 1298 if state.longitude>180.0: state.longitude = 360.0 - state.longitude 1299 1299 1300 1300 state.paused = data[self._monidx_paused]!=0 or \ 1301 1301 data[self._monidx_frozen]!=0 or \ … … 1309 1309 state.zfw = data[self._monidx_zfw] * const.LBSTOKG / 256.0 1310 1310 state.grossWeight = data[self._monidx_grossWeight] * const.LBSTOKG 1311 1311 1312 1312 state.heading = Handler.fsuipc2PositiveDegrees(data[self._monidx_heading]) 1313 1313 … … 1325 1325 1326 1326 state.gLoad = data[self._monidx_gLoad] / 625.0 1327 1327 1328 1328 numNotchesM1 = len(self._flapsNotches) - 1 1329 1329 flapsIncrement = 16383 / numNotchesM1 … … 1335 1335 flapsIndex += 1 1336 1336 state.flapsSet = self._flapsNotches[flapsIndex] 1337 1337 1338 1338 flapsLeft = data[self._monidx_flapsLeft] 1339 1339 state.flaps = self._flapsNotches[-1]*flapsLeft/16383.0 1340 1340 1341 1341 lights = data[self._monidx_lights] 1342 1342 1343 1343 state.navLightsOn = (lights&0x01) != 0 1344 1344 state.antiCollisionLightsOn = (lights&0x02) != 0 1345 1345 state.landingLightsOn = (lights&0x04) != 0 1346 1346 state.strobeLightsOn = (lights&0x10) != 0 1347 1347 1348 1348 state.pitotHeatOn = data[self._monidx_pitot]!=0 1349 1349 … … 1354 1354 1355 1355 state.spoilersArmed = data[self._monidx_spoilersArmed]!=0 1356 1356 1357 1357 spoilers = data[self._monidx_spoilers] 1358 1358 if spoilers<=4800: … … 1362 1362 1363 1363 state.altimeter = data[self._monidx_altimeter] / 16.0 1364 1364 1365 1365 state.nav1 = AircraftModel.convertFrequency(data[self._monidx_nav1]) 1366 1366 state.nav1_obs = data[self._monidx_nav1_obs] … … 1382 1382 1383 1383 state.visibility = data[self._monidx_visibility]*1609.344/100.0 1384 1384 1385 1385 state.cog = data[self._monidx_cog] 1386 1386 1387 1387 return state 1388 1388 … … 1392 1392 """A generic aircraft model that can handle the fuel levels, the N1 or RPM 1393 1393 values and some other common parameters in a generic way.""" 1394 1394 1395 1395 def __init__(self, flapsNotches, fuelTanks, numEngines, isN1 = True): 1396 1396 """Construct the generic aircraft model with the given data. … … 1416 1416 def doesHandle(self, aircraft, aircraftName): 1417 1417 """Determine if the model handles the given aircraft name. 1418 1418 1419 1419 This implementation returns True.""" 1420 1420 return True … … 1423 1423 """Add the model-specific monitoring data to the given array.""" 1424 1424 super(GenericAircraftModel, self).addMonitoringData(data, fsType) 1425 1425 1426 1426 self._fuelStartIndex = self._addFuelOffsets(data, "_monidx_fuelWeight") 1427 1427 … … 1434 1434 self._addOffsetWithIndexMember(data, 0x0898 + i * 0x98, "H") # RPM 1435 1435 self._addOffsetWithIndexMember(data, 0x08c8 + i * 0x98, "H") # RPM scaler 1436 1436 1437 1437 def getAircraftState(self, aircraft, timestamp, data): 1438 1438 """Get the aircraft state. 1439 1439 1440 Get it from the parent, and then add the data about the fuel levels and 1440 Get it from the parent, and then add the data about the fuel levels and 1441 1441 the engine parameters.""" 1442 1442 state = super(GenericAircraftModel, self).getAircraftState(aircraft, … … 1450 1450 state.rpm = None if self._isN1 else [] 1451 1451 itemsPerEngine = 2 if self._isN1 else 3 1452 1452 1453 1453 state.reverser = [] 1454 1454 for i in range(self._engineStartIndex, … … 1503 1503 self._addOffsetWithIndexMember(data, offset, "u") # tank level 1504 1504 self._addOffsetWithIndexMember(data, offset+4, "u") # tank capacity 1505 1505 1506 1506 return fuelStartIndex 1507 1507 … … 1523 1523 amount = data[index] * capacity / 128.0 / 65536.0 1524 1524 index += 2 1525 1526 result.append( (fuelTank, amount, capacity) if addCapacities 1525 1526 result.append( (fuelTank, amount, capacity) if addCapacities 1527 1527 else (fuelTank, amount)) 1528 1528 totalFuel += amount 1529 1529 1530 return (result, totalFuel) 1530 return (result, totalFuel) 1531 1531 1532 1532 def _handleFuelRetrieved(self, data, callback): 1533 1533 """Callback for a fuel retrieval request.""" 1534 (fuelData, _totalFuel) = self._convertFuelData(data, 1534 (fuelData, _totalFuel) = self._convertFuelData(data, 1535 1535 addCapacities = True) 1536 1536 callback(fuelData) 1537 1537 1538 1538 def _handleFuelWritten(self, success, extra): 1539 1539 """Callback for a fuel setting request.""" … … 1554 1554 def name(self): 1555 1555 """Get the name for this aircraft model.""" 1556 return "FSUIPC/Generic" 1556 return "FSUIPC/Generic" 1557 1557 1558 1558 #------------------------------------------------------------------------------ … … 1560 1560 class B737Model(GenericAircraftModel): 1561 1561 """Generic model for the Boeing 737 Classing and NG aircraft.""" 1562 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1563 1562 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1563 1564 1564 def __init__(self): 1565 1565 """Construct the model.""" … … 1601 1601 """Add the model-specific monitoring data to the given array.""" 1602 1602 self._fsType = fsType 1603 1603 1604 1604 super(PMDGBoeing737NGModel, self).addMonitoringData(data, fsType) 1605 1605 1606 1606 self._addOffsetWithIndexMember(data, 0x6202, "b", "_pmdgidx_switches") 1607 1607 … … 1630 1630 class B767Model(GenericAircraftModel): 1631 1631 """Generic model for the Boeing 767 aircraft.""" 1632 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1632 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1633 1633 1634 1634 def __init__(self): … … 1648 1648 class DH8DModel(GenericAircraftModel): 1649 1649 """Generic model for the Bombardier Dash 8-Q400 aircraft.""" 1650 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_RIGHT] 1651 1650 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_RIGHT] 1651 1652 1652 def __init__(self): 1653 1653 """Construct the model.""" … … 1696 1696 class CRJ2Model(GenericAircraftModel): 1697 1697 """Generic model for the Bombardier CRJ-200 aircraft.""" 1698 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1698 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1699 1699 1700 1700 def __init__(self): … … 1714 1714 class F70Model(GenericAircraftModel): 1715 1715 """Generic model for the Fokker F70 aircraft.""" 1716 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1716 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, const.FUELTANK_RIGHT] 1717 1717 1718 1718 def __init__(self): … … 1762 1762 """Generic model for the Lisunov Li-2 (DC-3) aircraft.""" 1763 1763 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_CENTRE, 1764 const.FUELTANK_RIGHT] 1764 const.FUELTANK_RIGHT] 1765 1765 # fuelTanks = [const.FUELTANK_LEFT_AUX, const.FUELTANK_LEFT, 1766 1766 # const.FUELTANK_RIGHT, const.FUELTANK_RIGHT_AUX] … … 1797 1797 self._leftLevel = self._rightLevel = \ 1798 1798 centreAmount / centreCapacity / 2.0 1799 fuelData = [(const.FUELTANK_LEFT_AUX, 1799 fuelData = [(const.FUELTANK_LEFT_AUX, 1800 1800 rawFuelData[0][1], rawFuelData[0][2]), 1801 1801 (const.FUELTANK_LEFT, … … 1822 1822 centreLevel = None 1823 1823 rightLevel = None 1824 1824 1825 1825 for (tank, level) in levels: 1826 1826 if tank==const.FUELTANK_LEFT_AUX: … … 1905 1905 """Generic model for the Yakovlev Yak-40 aircraft.""" 1906 1906 fuelTanks = [const.FUELTANK_LEFT, const.FUELTANK_RIGHT] 1907 1907 1908 1908 def __init__(self): 1909 1909 """Construct the model."""
Note:
See TracChangeset
for help on using the changeset viewer.